diff options
author | Greg Wilkins | 2013-04-15 01:06:06 +0000 |
---|---|---|
committer | Greg Wilkins | 2013-04-15 01:06:06 +0000 |
commit | 9a26992c8aa2e8a39822cd5590617484d1508c6f (patch) | |
tree | bd6edb433862ec462fa46722ab268732dc45fdac | |
parent | 70e6655ec5a5b45839fcd6b6bbe7be7427f5e8ef (diff) | |
download | org.eclipse.jetty.project-9a26992c8aa2e8a39822cd5590617484d1508c6f.tar.gz org.eclipse.jetty.project-9a26992c8aa2e8a39822cd5590617484d1508c6f.tar.xz org.eclipse.jetty.project-9a26992c8aa2e8a39822cd5590617484d1508c6f.zip |
402885 reuse Deflaters in GzipFilter
4 files changed, 155 insertions, 73 deletions
diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/GzipFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/GzipFilter.java index bd5a21b911..03752cb7f1 100644 --- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/GzipFilter.java +++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/GzipFilter.java @@ -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) + + switch (compressionType) + { + case GZIP: + return new GzipOutputStream(_response.getOutputStream(),_allocatedDeflater,_bufferSize); + case DEFLATE: + return new DeflaterOutputStream(_response.getOutputStream(),_allocatedDeflater,_bufferSize); + } + throw new IllegalStateException(compressionType + " not supported"); + } + + @Override + public void finish() throws IOException { - @Override - protected DeflaterOutputStream createStream() throws IOException + super.finish(); + if (_allocatedDeflater != null && _deflater.get() == null) { - return new DeflaterOutputStream(_response.getOutputStream(),new Deflater(_deflateCompressionLevel,_deflateNoWrap)); + _deflater.set(_allocatedDeflater); } - }; - } - }; - } - else - { - throw new IllegalStateException(compressionType + " not supported"); - } + } + }; + } + }; 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 { } } diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipHandler.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipHandler.java index 3ea1c2af69..ec125b9429 100644 --- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipHandler.java +++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipHandler.java @@ -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()) diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipOutputStream.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipOutputStream.java new file mode 100644 index 0000000000..d19bb008b1 --- /dev/null +++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipOutputStream.java @@ -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); + } + +} diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/PipelineHelper.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/PipelineHelper.java index 8e2fea7b56..95f0a23412 100644 --- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/PipelineHelper.java +++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/PipelineHelper.java @@ -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) { |