Skip to content

Commit

Permalink
Merge pull request #5248 from eclipse/jetty-10.0.x-5198-UpdateGzipHan…
Browse files Browse the repository at this point in the history
…dler

Issue #5198 - update gzip handler
  • Loading branch information
lachlan-roberts authored Sep 21, 2020
2 parents 7318443 + f2abf21 commit df085a6
Show file tree
Hide file tree
Showing 9 changed files with 234 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.component.Destroyable;
import org.eclipse.jetty.util.compression.InflaterPool;

/**
* <p>Decoder for the "gzip" content encoding.</p>
Expand All @@ -41,9 +42,10 @@ public class GZIPContentDecoder implements Destroyable
private static final long UINT_MAX = 0xFFFFFFFFL;

private final List<ByteBuffer> _inflateds = new ArrayList<>();
private final Inflater _inflater = new Inflater(true);
private final InflaterPool _inflaterPool;
private final ByteBufferPool _pool;
private final int _bufferSize;
private Inflater _inflater;
private State _state;
private int _size;
private long _value;
Expand All @@ -62,6 +64,13 @@ public GZIPContentDecoder(int bufferSize)

public GZIPContentDecoder(ByteBufferPool pool, int bufferSize)
{
this(null, pool, bufferSize);
}

public GZIPContentDecoder(InflaterPool inflaterPool, ByteBufferPool pool, int bufferSize)
{
_inflaterPool = inflaterPool;
_inflater = (inflaterPool == null) ? new Inflater(true) : inflaterPool.acquire();
_bufferSize = bufferSize;
_pool = pool;
reset();
Expand Down Expand Up @@ -207,8 +216,9 @@ else if ((_flags & 0x2) == 0x2)

try
{
int length = _inflater.inflate(buffer.array(), buffer.arrayOffset(), buffer.capacity());
buffer.limit(length);
int pos = BufferUtil.flipToFill(buffer);
_inflater.inflate(buffer);
BufferUtil.flipToFlush(buffer, pos);
}
catch (DataFormatException x)
{
Expand All @@ -226,23 +236,10 @@ else if (_inflater.needsInput())
{
if (!compressed.hasRemaining())
return;
if (compressed.hasArray())
{
_inflater.setInput(compressed.array(), compressed.arrayOffset() + compressed.position(), compressed.remaining());
compressed.position(compressed.limit());
}
else
{
// TODO use the pool
byte[] input = new byte[compressed.remaining()];
compressed.get(input);
_inflater.setInput(input);
}
_inflater.setInput(compressed);
}
else if (_inflater.finished())
{
int remaining = _inflater.getRemaining();
compressed.position(compressed.limit() - remaining);
_state = State.CRC;
_size = 0;
_value = 0;
Expand Down Expand Up @@ -386,7 +383,6 @@ else if (_inflater.finished())
if (_value != (_inflater.getBytesWritten() & UINT_MAX))
throw new ZipException("Invalid input size");

// TODO ByteBuffer result = output == null ? BufferUtil.EMPTY_BUFFER : ByteBuffer.wrap(output);
reset();
return;
}
Expand Down Expand Up @@ -420,7 +416,12 @@ private void reset()
@Override
public void destroy()
{
_inflater.end();
if (_inflaterPool == null)
_inflater.end();
else
_inflaterPool.release(_inflater);

_inflater = null;
}

public boolean isFinished()
Expand Down
6 changes: 6 additions & 0 deletions jetty-server/src/main/config/etc/jetty-gzip.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,16 @@
<Set name="checkGzExists" property="jetty.gzip.checkGzExists"/>
<Set name="compressionLevel" property="jetty.gzip.compressionLevel"/>
<Set name="inflateBufferSize" property="jetty.gzip.inflateBufferSize"/>
<Set name="inflaterPoolCapacity" property="jetty.gzip.inflaterPoolCapacity"/>
<Set name="deflaterPoolCapacity" property="jetty.gzip.deflaterPoolCapacity"/>
<Set name="syncFlush" property="jetty.gzip.syncFlush"/>
<Set name="dispatcherTypes" property="jetty.gzip.dispatcherTypes"/>
<Set name="includedMethodList" property="jetty.gzip.includedMethodList"/>
<Set name="excludedMethodList" property="jetty.gzip.excludedMethodList"/>
<Set name="includedMimeTypes" property="jetty.gzip.includedMimeTypeList"/>
<Set name="excludedMimeTypes" property="jetty.gzip.excludedMimeTypeList"/>
<Set name="includedPaths" property="jetty.gzip.includedPathList"/>
<Set name="excludedPaths" property="jetty.gzip.excludedPathList"/>

<!--
<Set name="includedMethods">
Expand Down
27 changes: 24 additions & 3 deletions jetty-server/src/main/config/modules/gzip.mod
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,32 @@ etc/jetty-gzip.xml
## Inflate request buffer size, or 0 for no request inflation
# jetty.gzip.inflateBufferSize=0

## Deflater pool max size (-1 for unlimited, 0 for no pool)
## Deflater pool max size (-1 for unlimited, 0 for no pooling)
# jetty.gzip.deflaterPoolCapacity=-1

## Comma separated list of included methods
## Inflater pool max size (-1 for unlimited, 0 for no pooling)
# jetty.gzip.inflaterPoolCapacity=-1

## Set the {@link Deflater} flush mode to use.
# jetty.gzip.syncFlush=false

## The set of DispatcherType that this filter will operate on
# jetty.gzip.dispatcherTypes=REQUEST

## Comma separated list of included HTTP methods
# jetty.gzip.includedMethodList=GET,POST

## Comma separated list of excluded methods
## Comma separated list of excluded HTTP methods
# jetty.gzip.excludedMethodList=

## Comma separated list of included MIME types
# jetty.gzip.includedMimeTypeList=

## Comma separated list of excluded MIME types
# jetty.gzip.excludedMimeTypeList=

## Comma separated list of included Path specs
# jetty.gzip.includedPathList=

## Comma separated list of excluded Path specs
# jetty.gzip.excludedPathList=
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import java.util.EnumSet;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.Deflater;
import javax.servlet.DispatcherType;
import javax.servlet.ServletContext;
Expand All @@ -42,9 +44,12 @@
import org.eclipse.jetty.server.HttpOutput;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.util.AsciiLowerCaseSet;
import org.eclipse.jetty.util.IncludeExclude;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.compression.CompressionPool;
import org.eclipse.jetty.util.compression.DeflaterPool;
import org.eclipse.jetty.util.compression.InflaterPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -158,8 +163,8 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
private static final HttpField TE_CHUNKED = new PreEncodedHttpField(HttpHeader.TRANSFER_ENCODING, HttpHeaderValue.CHUNKED.asString());
private static final Pattern COMMA_GZIP = Pattern.compile(".*, *gzip");

private int poolCapacity = -1;
private DeflaterPool _deflaterPool = null;
private final InflaterPool _inflaterPool;
private final DeflaterPool _deflaterPool;

private int _minGzipSize = DEFAULT_MIN_GZIP_SIZE;
private boolean _syncFlush = false;
Expand All @@ -168,7 +173,7 @@ public class GzipHandler extends HandlerWrapper implements GzipFactory
// non-static, as other GzipHandler instances may have different configurations
private final IncludeExclude<String> _methods = new IncludeExclude<>();
private final IncludeExclude<String> _paths = new IncludeExclude<>(PathSpecSet.class);
private final IncludeExclude<String> _mimeTypes = new IncludeExclude<>();
private final IncludeExclude<String> _mimeTypes = new IncludeExclude<>(AsciiLowerCaseSet.class);
private HttpField _vary = GzipHttpOutputInterceptor.VARY_ACCEPT_ENCODING;

/**
Expand Down Expand Up @@ -197,6 +202,11 @@ else if (type.startsWith("image/") ||

if (LOG.isDebugEnabled())
LOG.debug("{} mime types {}", this, _mimeTypes);

_deflaterPool = newDeflaterPool();
_inflaterPool = newInflaterPool();
addBean(_deflaterPool);
addBean(_inflaterPool);
}

/**
Expand Down Expand Up @@ -407,13 +417,6 @@ public void addIncludedPaths(String... pathspecs)
}
}

@Override
protected void doStart() throws Exception
{
_deflaterPool = newDeflaterPool(poolCapacity);
super.doStart();
}

@Override
public Deflater getDeflater(Request request, long contentLength)
{
Expand Down Expand Up @@ -564,7 +567,7 @@ public void handle(String target, Request baseRequest, HttpServletRequest reques
{
if (LOG.isDebugEnabled())
LOG.debug("{} inflate {}", this, request);
baseRequest.getHttpInput().addInterceptor(new GzipHttpInputInterceptor(baseRequest.getHttpChannel().getByteBufferPool(), _inflateBufferSize));
baseRequest.getHttpInput().addInterceptor(new GzipHttpInputInterceptor(_inflaterPool, baseRequest.getHttpChannel().getByteBufferPool(), _inflateBufferSize));
}

// Are we already being gzipped?
Expand Down Expand Up @@ -771,6 +774,19 @@ public void setExcludedPaths(String... pathspecs)
_paths.exclude(pathspecs);
}

/**
* Set of supported {@link DispatcherType} that this filter will operate on.
*
* @param dispatchers the set of {@link DispatcherType} that this filter will operate on
*/
public void setDispatcherTypes(String... dispatchers)
{
_dispatchers = EnumSet.copyOf(Stream.of(dispatchers)
.flatMap(s -> Stream.of(StringUtil.csvSplit(s)))
.map(DispatcherType::valueOf)
.collect(Collectors.toSet()));
}

/**
* Set the included filter list of HTTP methods (replacing any previously set)
*
Expand Down Expand Up @@ -876,7 +892,7 @@ public String getExcludedMethodList()
*/
public int getDeflaterPoolCapacity()
{
return poolCapacity;
return _deflaterPool.getCapacity();
}

/**
Expand All @@ -887,12 +903,38 @@ public void setDeflaterPoolCapacity(int capacity)
if (isStarted())
throw new IllegalStateException(getState());

poolCapacity = capacity;
_deflaterPool.setCapacity(capacity);
}

/**
* Gets the maximum number of Inflators that the DeflaterPool can hold.
*
* @return the Deflater pool capacity
*/
public int getInflaterPoolCapacity()
{
return _inflaterPool.getCapacity();
}

/**
* Sets the maximum number of Inflators that the DeflaterPool can hold.
*/
public void setInflaterPoolCapacity(int capacity)
{
if (isStarted())
throw new IllegalStateException(getState());

_inflaterPool.setCapacity(capacity);
}

protected InflaterPool newInflaterPool()
{
return new InflaterPool(CompressionPool.INFINITE_CAPACITY, true);
}

protected DeflaterPool newDeflaterPool(int capacity)
protected DeflaterPool newDeflaterPool()
{
return new DeflaterPool(capacity, Deflater.DEFAULT_COMPRESSION, true);
return new DeflaterPool(CompressionPool.INFINITE_CAPACITY, Deflater.DEFAULT_COMPRESSION, true);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.eclipse.jetty.server.HttpInput;
import org.eclipse.jetty.server.HttpInput.Content;
import org.eclipse.jetty.util.component.Destroyable;
import org.eclipse.jetty.util.compression.InflaterPool;

/**
* An HttpInput Interceptor that inflates GZIP encoded request content.
Expand All @@ -34,9 +35,9 @@ public class GzipHttpInputInterceptor implements HttpInput.Interceptor, Destroya
private final Decoder _decoder;
private ByteBuffer _chunk;

public GzipHttpInputInterceptor(ByteBufferPool pool, int bufferSize)
public GzipHttpInputInterceptor(InflaterPool inflaterPool, ByteBufferPool pool, int bufferSize)
{
_decoder = new Decoder(pool, bufferSize);
_decoder = new Decoder(inflaterPool, pool, bufferSize);
}

@Override
Expand Down Expand Up @@ -66,9 +67,9 @@ public void destroy()

private class Decoder extends GZIPContentDecoder
{
private Decoder(ByteBufferPool pool, int bufferSize)
private Decoder(InflaterPool inflaterPool, ByteBufferPool bufferPool, int bufferSize)
{
super(pool, bufferSize);
super(inflaterPool, bufferPool, bufferSize);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,6 @@ protected void commit(ByteBuffer content, boolean complete, Callback callback)
contentLength = content.remaining();

_deflater = _factory.getDeflater(_channel.getRequest(), contentLength);

if (_deflater == null)
{
LOG.debug("{} exclude no deflater", this);
Expand Down Expand Up @@ -355,7 +354,9 @@ protected Action process() throws Exception
int off = slice.arrayOffset() + slice.position();
int len = slice.remaining();
_crc.update(array, off, len);
_deflater.setInput(array, off, len); // TODO use ByteBuffer API in Jetty-10
// Ideally we would want to use the ByteBuffer API for Deflaters. However due the the ByteBuffer implementation
// of the CRC32.update() it is less efficient for us to use this rather than to convert to array ourselves.
_deflater.setInput(array, off, len);
slice.position(slice.position() + len);
if (_last && BufferUtil.isEmpty(_content))
_deflater.finish();
Expand Down
Loading

0 comments on commit df085a6

Please sign in to comment.