487197 - Deflater/Inflater memory leak with WebSocket permessage-deflate extension

+ CompressExtension implementations are now part of the Jetty LifeCycle
+ Deflater and Inflater implementations are only instantiated when
  needed.
+ CompressExtension.doStop() LifeCycle will call .end() on instantiated
  Deflater and Inflater implementations
This commit is contained in:
Joakim Erdfelt 2016-02-04 11:03:45 -07:00
parent 980ab316ca
commit cfe823a7d6
3 changed files with 42 additions and 9 deletions

View File

@ -23,7 +23,9 @@ import java.io.IOException;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.component.Dumpable;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.BatchMode;
@ -38,7 +40,7 @@ import org.eclipse.jetty.websocket.common.LogicalConnection;
import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
@ManagedObject("Abstract Extension")
public abstract class AbstractExtension extends ContainerLifeCycle implements Extension
public abstract class AbstractExtension extends AbstractLifeCycle implements Dumpable, Extension
{
private final Logger log;
private WebSocketPolicy policy;
@ -52,11 +54,15 @@ public abstract class AbstractExtension extends ContainerLifeCycle implements Ex
{
log = Log.getLogger(this.getClass());
}
@Override
public String dump()
{
return ContainerLifeCycle.dump(this);
}
public void dump(Appendable out, String indent) throws IOException
{
super.dump(out, indent);
// incoming
dumpWithHeading(out, indent, "incoming", this.nextIncoming);
dumpWithHeading(out, indent, "outgoing", this.nextOutgoing);

View File

@ -29,6 +29,7 @@ import org.eclipse.jetty.util.IteratingCallback;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.BatchMode;
@ -89,6 +90,11 @@ public class ExtensionStack extends ContainerLifeCycle implements IncomingFrames
Extension ext = exts.next();
ext.setNextOutgoingFrames(nextOutgoing);
nextOutgoing = ext;
if (ext instanceof LifeCycle)
{
addBean(ext,true);
}
}
// Connect incomings

View File

@ -74,28 +74,34 @@ public abstract class CompressExtension extends AbstractExtension
private final Queue<FrameEntry> entries = new ConcurrentArrayQueue<>();
private final IteratingCallback flusher = new Flusher();
private final Deflater deflater;
private final Inflater inflater;
private Deflater deflaterImpl;
private Inflater inflaterImpl;
protected AtomicInteger decompressCount = new AtomicInteger(0);
private int tailDrop = TAIL_DROP_NEVER;
private int rsvUse = RSV_USE_ALWAYS;
protected CompressExtension()
{
deflater = new Deflater(Deflater.DEFAULT_COMPRESSION,NOWRAP);
inflater = new Inflater(NOWRAP);
tailDrop = getTailDropMode();
rsvUse = getRsvUseMode();
}
public Deflater getDeflater()
{
return deflater;
if (deflaterImpl == null)
{
deflaterImpl = new Deflater(Deflater.DEFAULT_COMPRESSION,NOWRAP);
}
return deflaterImpl;
}
public Inflater getInflater()
{
return inflater;
if (inflaterImpl == null)
{
inflaterImpl = new Inflater(NOWRAP);
}
return inflaterImpl;
}
/**
@ -155,6 +161,8 @@ public abstract class CompressExtension extends AbstractExtension
}
byte[] output = new byte[DECOMPRESS_BUF_SIZE];
Inflater inflater = getInflater();
while(buf.hasRemaining() && inflater.needsInput())
{
if (!supplyInput(inflater,buf))
@ -346,6 +354,17 @@ public abstract class CompressExtension extends AbstractExtension
}
return true;
}
@Override
protected void doStop() throws Exception
{
LOG.info("doStop()");
if(deflaterImpl != null)
deflaterImpl.end();
if(inflaterImpl != null)
inflaterImpl.end();
super.doStop();
}
@Override
public String toString()
@ -429,6 +448,8 @@ public abstract class CompressExtension extends AbstractExtension
LOG.debug("Compressing {}: {} bytes in {} bytes chunk",entry,remaining,outputLength);
boolean needsCompress = true;
Deflater deflater = getDeflater();
if (deflater.needsInput() && !supplyInput(deflater,data))
{