402885 reuse Deflaters in GzipFilter

This commit is contained in:
Greg Wilkins 2013-04-15 11:06:06 +10:00
parent 70e6655ec5
commit 9a26992c8a
4 changed files with 156 additions and 74 deletions

View File

@ -26,26 +26,22 @@ 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.ServletResponseWrapper;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import org.eclipse.jetty.continuation.Continuation;
import org.eclipse.jetty.continuation.ContinuationListener;
import org.eclipse.jetty.continuation.ContinuationSupport;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.servlets.gzip.AbstractCompressedStream;
import org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.servlets.gzip.GzipOutputStream;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -129,6 +125,9 @@ public class GzipFilter extends UserAgentFilter
protected int _minGzipSize=256;
protected int _deflateCompressionLevel=Deflater.DEFAULT_COMPRESSION;
protected boolean _deflateNoWrap = true;
// non-static, as other GzipFilter instances may have different configurations
protected final ThreadLocal<Deflater> _deflater = new ThreadLocal<Deflater>();
protected final Set<String> _methods=new HashSet<String>();
protected Set<String> _excludedAgents;
@ -296,10 +295,10 @@ public class GzipFilter extends UserAgentFilter
}
finally
{
Continuation continuation = ContinuationSupport.getContinuation(request);
if (continuation.isSuspended() && continuation.isResponseWrapped())
if (request.isAsyncStarted())
{
continuation.addContinuationListener(new ContinuationListenerWaitingForWrappedResponseToFinish(wrappedResponse));
request.getAsyncContext().addListener(new FinishOnCompleteListener(wrappedResponse));
}
else if (exceptional && !response.isCommitted())
{
@ -403,64 +402,55 @@ public class GzipFilter extends UserAgentFilter
protected CompressedResponseWrapper createWrappedResponse(HttpServletRequest request, HttpServletResponse response, final String compressionType)
{
CompressedResponseWrapper wrappedResponse = null;
if (compressionType==null)
wrappedResponse = new CompressedResponseWrapper(request,response)
{
wrappedResponse = new CompressedResponseWrapper(request,response)
@Override
protected AbstractCompressedStream newCompressedStream(HttpServletRequest request, HttpServletResponse response) throws IOException
{
@Override
protected AbstractCompressedStream newCompressedStream(HttpServletRequest request,HttpServletResponse response) throws IOException
return new AbstractCompressedStream(compressionType,request,this,_vary)
{
return new AbstractCompressedStream(null,request,this,_vary)
private Deflater _allocatedDeflater;
@Override
protected DeflaterOutputStream createStream() throws IOException
{
@Override
protected DeflaterOutputStream createStream() throws IOException
if (compressionType == null)
{
return null;
}
};
}
};
}
else if (compressionType.equals(GZIP))
{
wrappedResponse = new CompressedResponseWrapper(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
// acquire deflater instance
_allocatedDeflater = _deflater.get();
if (_allocatedDeflater==null)
_allocatedDeflater = new Deflater(_deflateCompressionLevel,_deflateNoWrap);
else
{
return new GZIPOutputStream(_response.getOutputStream(),_bufferSize);
_deflater.remove();
_allocatedDeflater.reset();
}
};
}
};
}
else if (compressionType.equals(DEFLATE))
{
wrappedResponse = new CompressedResponseWrapper(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
switch (compressionType)
{
return new DeflaterOutputStream(_response.getOutputStream(),new Deflater(_deflateCompressionLevel,_deflateNoWrap));
case GZIP:
return new GzipOutputStream(_response.getOutputStream(),_allocatedDeflater,_bufferSize);
case DEFLATE:
return new DeflaterOutputStream(_response.getOutputStream(),_allocatedDeflater,_bufferSize);
}
};
}
};
}
else
{
throw new IllegalStateException(compressionType + " not supported");
}
throw new IllegalStateException(compressionType + " not supported");
}
@Override
public void finish() throws IOException
{
super.finish();
if (_allocatedDeflater != null && _deflater.get() == null)
{
_deflater.set(_allocatedDeflater);
}
}
};
}
};
configureWrappedResponse(wrappedResponse);
return wrappedResponse;
}
@ -472,18 +462,18 @@ public class GzipFilter extends UserAgentFilter
wrappedResponse.setMinCompressSize(_minGzipSize);
}
private class ContinuationListenerWaitingForWrappedResponseToFinish implements ContinuationListener
private class FinishOnCompleteListener implements AsyncListener
{
private CompressedResponseWrapper wrappedResponse;
public ContinuationListenerWaitingForWrappedResponseToFinish(CompressedResponseWrapper wrappedResponse)
public FinishOnCompleteListener(CompressedResponseWrapper wrappedResponse)
{
this.wrappedResponse = wrappedResponse;
}
@Override
public void onComplete(Continuation continuation)
{
public void onComplete(AsyncEvent event) throws IOException
{
try
{
wrappedResponse.finish();
@ -495,7 +485,17 @@ public class GzipFilter extends UserAgentFilter
}
@Override
public void onTimeout(Continuation continuation)
public void onTimeout(AsyncEvent event) throws IOException
{
}
@Override
public void onError(AsyncEvent event) throws IOException
{
}
@Override
public void onStartAsync(AsyncEvent event) throws IOException
{
}
}

View File

@ -29,13 +29,12 @@ import java.util.StringTokenizer;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.GZIPOutputStream;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.continuation.Continuation;
import org.eclipse.jetty.continuation.ContinuationListener;
import org.eclipse.jetty.continuation.ContinuationSupport;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.HandlerWrapper;
@ -265,12 +264,28 @@ public class GzipHandler extends HandlerWrapper
}
finally
{
Continuation continuation = ContinuationSupport.getContinuation(request);
if (continuation.isSuspended() && continuation.isResponseWrapped())
if (request.isAsyncStarted())
{
continuation.addContinuationListener(new ContinuationListener()
request.getAsyncContext().addListener(new AsyncListener()
{
public void onComplete(Continuation continuation)
@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
{
@ -281,9 +296,6 @@ public class GzipHandler extends HandlerWrapper
LOG.warn(e);
}
}
public void onTimeout(Continuation continuation)
{}
});
}
else if (exceptional && !response.isCommitted())

View File

@ -0,0 +1,70 @@
//
// ========================================================================
// Copyright (c) 1995-2013 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;
import java.util.zip.DeflaterOutputStream;
/**
* Reimplementation of {@link java.util.zip.GZIPOutputStream} that supports reusing a {@link Deflater} instance.
*/
public class GzipOutputStream extends DeflaterOutputStream
{
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, int size) throws IOException
{
super(out,deflater,size);
out.write(GZIP_HEADER);
}
public synchronized void write(byte[] buf, int off, int len) throws IOException
{
super.write(buf,off,len);
_crc.update(buf,off,len);
}
public 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);
}
}

View File

@ -219,8 +219,8 @@ public class PipelineHelper
int val = inputStream.read();
try
{
if (left % 10 == 0)
Thread.sleep(1);
if (left % 1000 == 0)
Thread.sleep(10);
}
catch (InterruptedException e)
{