423392 - GzipFilter without wrapping or blocking
I Added AsyncGzipFilter, which uses a modified HttpOutput instance to provide gzip compression without wrapping or blocking. Does not currently handle deflate.
This commit is contained in:
parent
5f204b8812
commit
cd05751ff7
|
@ -98,7 +98,6 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
|
|||
private final HttpChannelState _state;
|
||||
private final Request _request;
|
||||
private final Response _response;
|
||||
private final BlockingCallback _writeblock=new BlockingCallback();
|
||||
private HttpVersion _version = HttpVersion.HTTP_1_1;
|
||||
private boolean _expect = false;
|
||||
private boolean _expect100Continue = false;
|
||||
|
@ -127,12 +126,6 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
|
|||
{
|
||||
return _version;
|
||||
}
|
||||
|
||||
BlockingCallback getWriteBlockingCallback()
|
||||
{
|
||||
return _writeblock;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of requests handled by this connection
|
||||
*/
|
||||
|
@ -725,29 +718,17 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
|
|||
|
||||
protected boolean sendResponse(ResponseInfo info, ByteBuffer content, boolean complete) throws IOException
|
||||
{
|
||||
boolean committing=sendResponse(info,content,complete,_writeblock);
|
||||
_writeblock.block();
|
||||
BlockingCallback writeBlock = _response.getHttpOutput().getWriteBlockingCallback();
|
||||
boolean committing=sendResponse(info,content,complete,writeBlock);
|
||||
writeBlock.block();
|
||||
return committing;
|
||||
}
|
||||
|
||||
protected boolean isCommitted()
|
||||
public boolean isCommitted()
|
||||
{
|
||||
return _committed.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Blocking write, committing the response if needed.</p>
|
||||
*
|
||||
* @param content the content buffer to write
|
||||
* @param complete whether the content is complete for the response
|
||||
* @throws IOException if the write fails
|
||||
*/
|
||||
protected void write(ByteBuffer content, boolean complete) throws IOException
|
||||
{
|
||||
sendResponse(null,content,complete,_writeblock);
|
||||
_writeblock.block();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Non-Blocking write, committing the response if needed.</p>
|
||||
*
|
||||
|
|
|
@ -509,6 +509,8 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
|||
case NEED_HEADER:
|
||||
{
|
||||
// Look for optimisation to avoid allocating a _header buffer
|
||||
/*
|
||||
Cannot use this optimisation unless we work out how not to overwrite data in user passed arrays.
|
||||
if (_lastContent && _content!=null && !_content.isReadOnly() && _content.hasArray() && BufferUtil.space(_content)>_config.getResponseHeaderSize() )
|
||||
{
|
||||
// use spare space in content buffer for header buffer
|
||||
|
@ -522,7 +524,9 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
|
|||
_content.limit(l);
|
||||
}
|
||||
else
|
||||
*/
|
||||
_header = _bufferPool.acquire(_config.getResponseHeaderSize(), HEADER_BUFFER_DIRECT);
|
||||
|
||||
continue;
|
||||
}
|
||||
case NEED_CHUNK:
|
||||
|
|
|
@ -55,6 +55,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
{
|
||||
private static Logger LOG = Log.getLogger(HttpOutput.class);
|
||||
private final HttpChannel<?> _channel;
|
||||
private final BlockingCallback _writeblock=new BlockingCallback();
|
||||
private long _written;
|
||||
private ByteBuffer _aggregate;
|
||||
private int _bufferSize;
|
||||
|
@ -80,7 +81,12 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
_bufferSize = _channel.getHttpConfiguration().getOutputBufferSize();
|
||||
_commitSize=_bufferSize/4;
|
||||
}
|
||||
|
||||
|
||||
public HttpChannel<?> getHttpChannel()
|
||||
{
|
||||
return _channel;
|
||||
}
|
||||
|
||||
public boolean isWritten()
|
||||
{
|
||||
return _written > 0;
|
||||
|
@ -107,6 +113,22 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
return _channel.getResponse().isAllContentWritten(_written);
|
||||
}
|
||||
|
||||
protected BlockingCallback getWriteBlockingCallback()
|
||||
{
|
||||
return _writeblock;
|
||||
}
|
||||
|
||||
protected void write(ByteBuffer content, boolean complete) throws IOException
|
||||
{
|
||||
write(content,complete,_writeblock);
|
||||
_writeblock.block();
|
||||
}
|
||||
|
||||
protected void write(ByteBuffer content, boolean complete, Callback callback)
|
||||
{
|
||||
_channel.write(content,complete,callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
|
@ -117,10 +139,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
{
|
||||
try
|
||||
{
|
||||
if (BufferUtil.hasContent(_aggregate))
|
||||
_channel.write(_aggregate, !_channel.getResponse().isIncluding());
|
||||
else
|
||||
_channel.write(BufferUtil.EMPTY_BUFFER, !_channel.getResponse().isIncluding());
|
||||
write(BufferUtil.hasContent(_aggregate)?_aggregate:BufferUtil.EMPTY_BUFFER,!_channel.getResponse().isIncluding());
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
|
@ -180,10 +199,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
switch(_state.get())
|
||||
{
|
||||
case OPEN:
|
||||
if (BufferUtil.hasContent(_aggregate))
|
||||
_channel.write(_aggregate, false);
|
||||
else
|
||||
_channel.write(BufferUtil.EMPTY_BUFFER, false);
|
||||
write(BufferUtil.hasContent(_aggregate)?_aggregate:BufferUtil.EMPTY_BUFFER, false);
|
||||
return;
|
||||
|
||||
case ASYNC:
|
||||
|
@ -290,7 +306,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
// flush any content from the aggregate
|
||||
if (BufferUtil.hasContent(_aggregate))
|
||||
{
|
||||
_channel.write(_aggregate, complete && len==0);
|
||||
write(_aggregate, complete && len==0);
|
||||
|
||||
// should we fill aggregate again from the buffer?
|
||||
if (len>0 && !complete && len<=_commitSize)
|
||||
|
@ -303,26 +319,25 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
// write any remaining content in the buffer directly
|
||||
if (len>0)
|
||||
{
|
||||
// pass as readonly to avoid space stealing optimisation in HttpConnection
|
||||
ByteBuffer wrap = ByteBuffer.wrap(b, off, len);
|
||||
ByteBuffer readonly = wrap.asReadOnlyBuffer();
|
||||
ByteBuffer view = wrap.duplicate();
|
||||
|
||||
// write a buffer capacity at a time to avoid JVM pooling large direct buffers
|
||||
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6210541
|
||||
while (len>getBufferSize())
|
||||
{
|
||||
int p=readonly.position();
|
||||
int p=view.position();
|
||||
int l=p+getBufferSize();
|
||||
readonly.limit(p+getBufferSize());
|
||||
_channel.write(readonly,false);
|
||||
view.limit(p+getBufferSize());
|
||||
write(view,false);
|
||||
len-=getBufferSize();
|
||||
readonly.limit(l+Math.min(len,getBufferSize()));
|
||||
readonly.position(l);
|
||||
view.limit(l+Math.min(len,getBufferSize()));
|
||||
view.position(l);
|
||||
}
|
||||
_channel.write(readonly,complete);
|
||||
write(view,complete);
|
||||
}
|
||||
else if (complete)
|
||||
_channel.write(BufferUtil.EMPTY_BUFFER,complete);
|
||||
write(BufferUtil.EMPTY_BUFFER,complete);
|
||||
|
||||
if (complete)
|
||||
closed();
|
||||
|
@ -370,13 +385,13 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
|
||||
// flush any content from the aggregate
|
||||
if (BufferUtil.hasContent(_aggregate))
|
||||
_channel.write(_aggregate, complete && len==0);
|
||||
write(_aggregate, complete && len==0);
|
||||
|
||||
// write any remaining content in the buffer directly
|
||||
if (len>0)
|
||||
_channel.write(buffer, complete);
|
||||
write(buffer, complete);
|
||||
else if (complete)
|
||||
_channel.write(BufferUtil.EMPTY_BUFFER,complete);
|
||||
write(BufferUtil.EMPTY_BUFFER,complete);
|
||||
|
||||
if (complete)
|
||||
closed();
|
||||
|
@ -401,9 +416,8 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
// Check if all written or full
|
||||
if (complete || BufferUtil.isFull(_aggregate))
|
||||
{
|
||||
BlockingCallback callback = _channel.getWriteBlockingCallback();
|
||||
_channel.write(_aggregate, complete, callback);
|
||||
callback.block();
|
||||
write(_aggregate, complete, _writeblock);
|
||||
_writeblock.block();
|
||||
if (complete)
|
||||
closed();
|
||||
}
|
||||
|
@ -443,8 +457,6 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void print(String s) throws IOException
|
||||
{
|
||||
|
@ -461,11 +473,8 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
*/
|
||||
public void sendContent(ByteBuffer content) throws IOException
|
||||
{
|
||||
final BlockingCallback callback =_channel.getWriteBlockingCallback();
|
||||
if (content.hasArray()&&content.limit()<content.capacity())
|
||||
content=content.asReadOnlyBuffer();
|
||||
_channel.write(content,true,callback);
|
||||
callback.block();
|
||||
write(content,true,_writeblock);
|
||||
_writeblock.block();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -475,9 +484,8 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
*/
|
||||
public void sendContent(InputStream in) throws IOException
|
||||
{
|
||||
final BlockingCallback callback =_channel.getWriteBlockingCallback();
|
||||
new InputStreamWritingCB(in,callback).iterate();
|
||||
callback.block();
|
||||
new InputStreamWritingCB(in,_writeblock).iterate();
|
||||
_writeblock.block();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -487,9 +495,8 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
*/
|
||||
public void sendContent(ReadableByteChannel in) throws IOException
|
||||
{
|
||||
final BlockingCallback callback =_channel.getWriteBlockingCallback();
|
||||
new ReadableByteChannelWritingCB(in,callback).iterate();
|
||||
callback.block();
|
||||
new ReadableByteChannelWritingCB(in,_writeblock).iterate();
|
||||
_writeblock.block();
|
||||
}
|
||||
|
||||
|
||||
|
@ -500,9 +507,8 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
*/
|
||||
public void sendContent(HttpContent content) throws IOException
|
||||
{
|
||||
final BlockingCallback callback =_channel.getWriteBlockingCallback();
|
||||
sendContent(content,callback);
|
||||
callback.block();
|
||||
sendContent(content,_writeblock);
|
||||
_writeblock.block();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -512,9 +518,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
*/
|
||||
public void sendContent(ByteBuffer content, final Callback callback)
|
||||
{
|
||||
if (content.hasArray()&&content.limit()<content.capacity())
|
||||
content=content.asReadOnlyBuffer();
|
||||
_channel.write(content,true,new Callback()
|
||||
write(content,true,new Callback()
|
||||
{
|
||||
@Override
|
||||
public void succeeded()
|
||||
|
@ -759,14 +763,14 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
if (BufferUtil.hasContent(_aggregate))
|
||||
{
|
||||
_flushed=true;
|
||||
_channel.write(_aggregate, false, this);
|
||||
write(_aggregate, false, this);
|
||||
return State.SCHEDULED;
|
||||
}
|
||||
|
||||
if (!_flushed)
|
||||
{
|
||||
_flushed=true;
|
||||
_channel.write(BufferUtil.EMPTY_BUFFER,false,this);
|
||||
write(BufferUtil.EMPTY_BUFFER,false,this);
|
||||
return State.SCHEDULED;
|
||||
}
|
||||
|
||||
|
@ -789,7 +793,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
_buffer=ByteBuffer.wrap(b, off, len);
|
||||
_len=len;
|
||||
// always use a view for large byte arrays to avoid JVM pooling large direct buffers
|
||||
_slice=_len<getBufferSize()?null:_buffer.asReadOnlyBuffer();
|
||||
_slice=_len<getBufferSize()?null:_buffer.duplicate();
|
||||
_complete=complete;
|
||||
}
|
||||
|
||||
|
@ -798,7 +802,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
_buffer=buffer;
|
||||
_len=buffer.remaining();
|
||||
// Use a slice buffer for large indirect to avoid JVM pooling large direct buffers
|
||||
_slice=_buffer.isDirect()||_len<getBufferSize()?null:_buffer.asReadOnlyBuffer();
|
||||
_slice=_buffer.isDirect()||_len<getBufferSize()?null:_buffer.duplicate();
|
||||
_complete=complete;
|
||||
}
|
||||
|
||||
|
@ -809,7 +813,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
if (BufferUtil.hasContent(_aggregate))
|
||||
{
|
||||
_completed=_len==0;
|
||||
_channel.write(_aggregate, _complete && _completed, this);
|
||||
write(_aggregate, _complete && _completed, this);
|
||||
return State.SCHEDULED;
|
||||
}
|
||||
|
||||
|
@ -827,7 +831,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
if (_slice==null)
|
||||
{
|
||||
_completed=true;
|
||||
_channel.write(_buffer, _complete, this);
|
||||
write(_buffer, _complete, this);
|
||||
return State.SCHEDULED;
|
||||
}
|
||||
|
||||
|
@ -839,7 +843,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
_buffer.position(pl);
|
||||
_slice.position(p);
|
||||
_completed=!_buffer.hasRemaining();
|
||||
_channel.write(_slice, _complete && _completed, this);
|
||||
write(_slice, _complete && _completed, this);
|
||||
return State.SCHEDULED;
|
||||
}
|
||||
|
||||
|
@ -850,7 +854,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
if (!_completed)
|
||||
{
|
||||
_completed=true;
|
||||
_channel.write(BufferUtil.EMPTY_BUFFER, _complete, this);
|
||||
write(BufferUtil.EMPTY_BUFFER, _complete, this);
|
||||
return State.SCHEDULED;
|
||||
}
|
||||
closed();
|
||||
|
@ -910,7 +914,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
// write what we have
|
||||
_buffer.position(0);
|
||||
_buffer.limit(len);
|
||||
_channel.write(_buffer,_eof,this);
|
||||
write(_buffer,_eof,this);
|
||||
return State.SCHEDULED;
|
||||
}
|
||||
|
||||
|
@ -973,7 +977,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable
|
|||
|
||||
// write what we have
|
||||
_buffer.flip();
|
||||
_channel.write(_buffer,_eof,this);
|
||||
write(_buffer,_eof,this);
|
||||
|
||||
return State.SCHEDULED;
|
||||
}
|
||||
|
|
|
@ -109,9 +109,9 @@ public class Response implements HttpServletResponse
|
|||
public final static String HTTP_ONLY_COMMENT = "__HTTP_ONLY__";
|
||||
|
||||
private final HttpChannel<?> _channel;
|
||||
private final HttpOutput _out;
|
||||
private final HttpFields _fields = new HttpFields();
|
||||
private final AtomicInteger _include = new AtomicInteger();
|
||||
private HttpOutput _out;
|
||||
private int _status = HttpStatus.NOT_SET_000;
|
||||
private String _reason;
|
||||
private Locale _locale;
|
||||
|
@ -179,6 +179,11 @@ public class Response implements HttpServletResponse
|
|||
{
|
||||
return _out;
|
||||
}
|
||||
|
||||
public void setHttpOutput(HttpOutput out)
|
||||
{
|
||||
_out=out;
|
||||
}
|
||||
|
||||
public boolean isIncluding()
|
||||
{
|
||||
|
@ -988,15 +993,14 @@ public class Response implements HttpServletResponse
|
|||
if (isCommitted() || isIncluding())
|
||||
return;
|
||||
|
||||
long written = _out.getWritten();
|
||||
if (written > len)
|
||||
throw new IllegalArgumentException("setContentLength(" + len + ") when already written " + written);
|
||||
|
||||
_contentLength = len;
|
||||
_fields.putLongField(HttpHeader.CONTENT_LENGTH.toString(), len);
|
||||
|
||||
if (_contentLength > 0)
|
||||
{
|
||||
long written = _out.getWritten();
|
||||
if (written > len)
|
||||
throw new IllegalArgumentException("setContentLength(" + len + ") when already written " + written);
|
||||
|
||||
_fields.putLongField(HttpHeader.CONTENT_LENGTH, len);
|
||||
if (isAllContentWritten(written))
|
||||
{
|
||||
try
|
||||
|
@ -1009,6 +1013,20 @@ public class Response implements HttpServletResponse
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (_contentLength==0)
|
||||
{
|
||||
long written = _out.getWritten();
|
||||
if (written > 0)
|
||||
throw new IllegalArgumentException("setContentLength(0) when already written " + written);
|
||||
_fields.put(HttpHeader.CONTENT_LENGTH, "0");
|
||||
}
|
||||
else
|
||||
_fields.remove(HttpHeader.CONTENT_LENGTH);
|
||||
}
|
||||
|
||||
public long getContentLength()
|
||||
{
|
||||
return _contentLength;
|
||||
}
|
||||
|
||||
public boolean isAllContentWritten(long written)
|
||||
|
|
|
@ -882,7 +882,10 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
|
|||
@Override
|
||||
public void failed(Throwable x)
|
||||
{
|
||||
LOG.debug(x);
|
||||
if (x instanceof IOException)
|
||||
LOG.debug(x);
|
||||
else
|
||||
LOG.warn(x);
|
||||
context.complete();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -41,6 +41,17 @@ public class ServletTester extends ContainerLifeCycle
|
|||
private final Server _server=new Server();
|
||||
private final LocalConnector _connector=new LocalConnector(_server);
|
||||
private final ServletContextHandler _context;
|
||||
|
||||
public Server getServer()
|
||||
{
|
||||
return _server;
|
||||
}
|
||||
|
||||
public LocalConnector getConnector()
|
||||
{
|
||||
return _connector;
|
||||
}
|
||||
|
||||
public void setVirtualHosts(String[] vhosts)
|
||||
{
|
||||
_context.setVirtualHosts(vhosts);
|
||||
|
|
|
@ -0,0 +1,535 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.Deflater;
|
||||
|
||||
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.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.eclipse.jetty.http.HttpGenerator;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.MimeTypes;
|
||||
import org.eclipse.jetty.server.HttpChannel;
|
||||
import org.eclipse.jetty.server.HttpOutput;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.servlets.gzip.GzipFactory;
|
||||
import org.eclipse.jetty.servlets.gzip.GzipHttpOutput;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Async GZIP Filter
|
||||
* This filter is a gzip filter using jetty internal mechanism to apply gzip compression
|
||||
* to output that is compatible with async IO and does not need to wrap the response nor output stream.
|
||||
* The filter will gzip the content of a response if: <ul>
|
||||
* <li>The filter is mapped to a matching path</li>
|
||||
* <li>accept-encoding header is set to either gzip, deflate or a combination of those</li>
|
||||
* <li>The response status code is >=200 and <300
|
||||
* <li>The content length is unknown or more than the <code>minGzipSize</code> initParameter or the minGzipSize is 0(default)</li>
|
||||
* <li>If a list of mimeTypes is set by the <code>mimeTypes</code> init parameter, then the Content-Type is in the list.</li>
|
||||
* <li>If no mimeType list is set, then the content-type is not in the list defined by <code>excludedMimeTypes</code></li>
|
||||
* <li>No content-encoding is specified by the resource</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* Compressing the content can greatly improve the network bandwidth usage, but at a cost of memory and
|
||||
* CPU cycles. If this filter is mapped for static content, then use of efficient direct NIO may be
|
||||
* prevented, thus use of the gzip mechanism of the {@link org.eclipse.jetty.servlet.DefaultServlet} is
|
||||
* advised instead.
|
||||
* </p>
|
||||
* <p>
|
||||
* This filter extends {@link UserAgentFilter} and if the the initParameter <code>excludedAgents</code>
|
||||
* is set to a comma separated list of user agents, then these agents will be excluded from gzip content.
|
||||
* </p>
|
||||
* <p>Init Parameters:</p>
|
||||
* <dl>
|
||||
* <dt>bufferSize</dt> <dd>The output buffer size. Defaults to 8192. Be careful as values <= 0 will lead to an
|
||||
* {@link IllegalArgumentException}.
|
||||
* See: {@link java.util.zip.GZIPOutputStream#GZIPOutputStream(java.io.OutputStream, int)}
|
||||
* and: {@link java.util.zip.DeflaterOutputStream#DeflaterOutputStream(java.io.OutputStream, Deflater, int)}
|
||||
* </dd>
|
||||
* <dt>minGzipSize</dt> <dd>Content will only be compressed if content length is either unknown or greater
|
||||
* than <code>minGzipSize</code>.
|
||||
* </dd>
|
||||
* <dt>deflateCompressionLevel</dt> <dd>The compression level used for deflate compression. (0-9).
|
||||
* See: {@link java.util.zip.Deflater#Deflater(int, boolean)}
|
||||
* </dd>
|
||||
* <dt>deflateNoWrap</dt> <dd>The noWrap setting for deflate compression. Defaults to true. (true/false)
|
||||
* See: {@link java.util.zip.Deflater#Deflater(int, boolean)}
|
||||
* </dd>
|
||||
* <dt>methods</dt> <dd>Comma separated list of HTTP methods to compress. If not set, only GET requests are compressed.
|
||||
* </dd>
|
||||
* <dt>mimeTypes</dt> <dd>Comma separated list of mime types to compress. If it is not set, then the excludedMimeTypes list is used.
|
||||
* </dd>
|
||||
* <dt>excludedMimeTypes</dt> <dd>Comma separated list of mime types to never compress. If not set, then the default is the commonly known
|
||||
* image, video, audio and compressed types.
|
||||
* </dd>
|
||||
|
||||
* <dt>excludedAgents</dt> <dd>Comma separated list of user agents to exclude from compression. Does a
|
||||
* {@link String#contains(CharSequence)} to check if the excluded agent occurs
|
||||
* in the user-agent header. If it does -> no compression
|
||||
* </dd>
|
||||
* <dt>excludeAgentPatterns</dt> <dd>Same as excludedAgents, but accepts regex patterns for more complex matching.
|
||||
* </dd>
|
||||
* <dt>excludePaths</dt> <dd>Comma separated list of paths to exclude from compression.
|
||||
* Does a {@link String#startsWith(String)} comparison to check if the path matches.
|
||||
* If it does match -> no compression. To match subpaths use <code>excludePathPatterns</code>
|
||||
* instead.
|
||||
* </dd>
|
||||
* <dt>excludePathPatterns</dt> <dd>Same as excludePath, but accepts regex patterns for more complex matching.
|
||||
* </dd>
|
||||
* <dt>vary</dt> <dd>Set to the value of the Vary header sent with responses that could be compressed. By default it is
|
||||
* set to 'Vary: Accept-Encoding, User-Agent' since IE6 is excluded by default from the excludedAgents.
|
||||
* If user-agents are not to be excluded, then this can be set to 'Vary: Accept-Encoding'. Note also
|
||||
* that shared caches may cache copies of a resource that is varied by User-Agent - one per variation of
|
||||
* the User-Agent, unless the cache does some normalization of the UA string.
|
||||
* </dd>
|
||||
* <dt>checkGzExists</dt> <dd>If set to true, the filter check if a static resource with ".gz" appended exists. If so then
|
||||
* the normal processing is done so that the default servlet can send the pre existing gz content.
|
||||
* </dd>
|
||||
* </dl>
|
||||
*/
|
||||
public class AsyncGzipFilter extends UserAgentFilter implements GzipFactory
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(GzipFilter.class);
|
||||
public final static String GZIP = "gzip";
|
||||
public static final String DEFLATE = "deflate";
|
||||
public final static String ETAG = "o.e.j.s.GzipFilter.ETag";
|
||||
public final static int DEFAULT_MIN_GZIP_SIZE=256;
|
||||
|
||||
protected ServletContext _context;
|
||||
protected final Set<String> _mimeTypes=new HashSet<>();
|
||||
protected boolean _excludeMimeTypes;
|
||||
protected int _bufferSize=8192;
|
||||
protected int _minGzipSize=DEFAULT_MIN_GZIP_SIZE;
|
||||
protected int _deflateCompressionLevel=Deflater.DEFAULT_COMPRESSION;
|
||||
protected boolean _deflateNoWrap = true;
|
||||
protected boolean _checkGzExists = true;
|
||||
|
||||
// non-static, as other GzipFilter instances may have different configurations
|
||||
protected final ThreadLocal<Deflater> _deflater = new ThreadLocal<Deflater>();
|
||||
|
||||
protected final static ThreadLocal<byte[]> _buffer= new ThreadLocal<byte[]>();
|
||||
|
||||
protected final Set<String> _methods=new HashSet<String>();
|
||||
protected Set<String> _excludedAgents;
|
||||
protected Set<Pattern> _excludedAgentPatterns;
|
||||
protected Set<String> _excludedPaths;
|
||||
protected Set<Pattern> _excludedPathPatterns;
|
||||
protected HttpField _vary=new HttpGenerator.CachedHttpField(HttpHeader.VARY,HttpHeader.ACCEPT_ENCODING+", "+HttpHeader.USER_AGENT);
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @see org.eclipse.jetty.servlets.UserAgentFilter#init(javax.servlet.FilterConfig)
|
||||
*/
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException
|
||||
{
|
||||
super.init(filterConfig);
|
||||
|
||||
_context=filterConfig.getServletContext();
|
||||
|
||||
String tmp=filterConfig.getInitParameter("bufferSize");
|
||||
if (tmp!=null)
|
||||
_bufferSize=Integer.parseInt(tmp);
|
||||
LOG.debug("{} bufferSize={}",this,_bufferSize);
|
||||
|
||||
tmp=filterConfig.getInitParameter("minGzipSize");
|
||||
if (tmp!=null)
|
||||
_minGzipSize=Integer.parseInt(tmp);
|
||||
LOG.debug("{} minGzipSize={}",this,_minGzipSize);
|
||||
|
||||
tmp=filterConfig.getInitParameter("deflateCompressionLevel");
|
||||
if (tmp!=null)
|
||||
_deflateCompressionLevel=Integer.parseInt(tmp);
|
||||
LOG.debug("{} deflateCompressionLevel={}",this,_deflateCompressionLevel);
|
||||
|
||||
tmp=filterConfig.getInitParameter("deflateNoWrap");
|
||||
if (tmp!=null)
|
||||
_deflateNoWrap=Boolean.parseBoolean(tmp);
|
||||
LOG.debug("{} deflateNoWrap={}",this,_deflateNoWrap);
|
||||
|
||||
tmp=filterConfig.getInitParameter("checkGzExists");
|
||||
if (tmp!=null)
|
||||
_checkGzExists=Boolean.parseBoolean(tmp);
|
||||
LOG.debug("{} checkGzExists={}",this,_checkGzExists);
|
||||
|
||||
tmp=filterConfig.getInitParameter("methods");
|
||||
if (tmp!=null)
|
||||
{
|
||||
StringTokenizer tok = new StringTokenizer(tmp,",",false);
|
||||
while (tok.hasMoreTokens())
|
||||
_methods.add(tok.nextToken().trim().toUpperCase());
|
||||
}
|
||||
else
|
||||
_methods.add(HttpMethod.GET.asString());
|
||||
LOG.debug("{} methods={}",this,_methods);
|
||||
|
||||
tmp=filterConfig.getInitParameter("mimeTypes");
|
||||
if (tmp==null)
|
||||
{
|
||||
_excludeMimeTypes=true;
|
||||
tmp=filterConfig.getInitParameter("excludedMimeTypes");
|
||||
if (tmp==null)
|
||||
{
|
||||
for (String type:MimeTypes.getKnownMimeTypes())
|
||||
{
|
||||
if (type.startsWith("image/")||
|
||||
type.startsWith("audio/")||
|
||||
type.startsWith("video/"))
|
||||
_mimeTypes.add(type);
|
||||
_mimeTypes.add("application/compress");
|
||||
_mimeTypes.add("application/zip");
|
||||
_mimeTypes.add("application/gzip");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
StringTokenizer tok = new StringTokenizer(tmp,",",false);
|
||||
while (tok.hasMoreTokens())
|
||||
_mimeTypes.add(tok.nextToken().trim());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
StringTokenizer tok = new StringTokenizer(tmp,",",false);
|
||||
while (tok.hasMoreTokens())
|
||||
_mimeTypes.add(tok.nextToken().trim());
|
||||
}
|
||||
LOG.debug("{} mimeTypes={}",this,_mimeTypes);
|
||||
LOG.debug("{} excludeMimeTypes={}",this,_excludeMimeTypes);
|
||||
tmp=filterConfig.getInitParameter("excludedAgents");
|
||||
if (tmp!=null)
|
||||
{
|
||||
_excludedAgents=new HashSet<String>();
|
||||
StringTokenizer tok = new StringTokenizer(tmp,",",false);
|
||||
while (tok.hasMoreTokens())
|
||||
_excludedAgents.add(tok.nextToken().trim());
|
||||
}
|
||||
LOG.debug("{} excludedAgents={}",this,_excludedAgents);
|
||||
|
||||
tmp=filterConfig.getInitParameter("excludeAgentPatterns");
|
||||
if (tmp!=null)
|
||||
{
|
||||
_excludedAgentPatterns=new HashSet<Pattern>();
|
||||
StringTokenizer tok = new StringTokenizer(tmp,",",false);
|
||||
while (tok.hasMoreTokens())
|
||||
_excludedAgentPatterns.add(Pattern.compile(tok.nextToken().trim()));
|
||||
}
|
||||
LOG.debug("{} excludedAgentPatterns={}",this,_excludedAgentPatterns);
|
||||
|
||||
tmp=filterConfig.getInitParameter("excludePaths");
|
||||
if (tmp!=null)
|
||||
{
|
||||
_excludedPaths=new HashSet<String>();
|
||||
StringTokenizer tok = new StringTokenizer(tmp,",",false);
|
||||
while (tok.hasMoreTokens())
|
||||
_excludedPaths.add(tok.nextToken().trim());
|
||||
}
|
||||
LOG.debug("{} excludedPaths={}",this,_excludedPaths);
|
||||
|
||||
tmp=filterConfig.getInitParameter("excludePathPatterns");
|
||||
if (tmp!=null)
|
||||
{
|
||||
_excludedPathPatterns=new HashSet<Pattern>();
|
||||
StringTokenizer tok = new StringTokenizer(tmp,",",false);
|
||||
while (tok.hasMoreTokens())
|
||||
_excludedPathPatterns.add(Pattern.compile(tok.nextToken().trim()));
|
||||
}
|
||||
LOG.debug("{} excludedPathPatterns={}",this,_excludedPathPatterns);
|
||||
|
||||
tmp=filterConfig.getInitParameter("vary");
|
||||
if (tmp!=null)
|
||||
_vary=new HttpGenerator.CachedHttpField(HttpHeader.VARY,tmp);
|
||||
LOG.debug("{} vary={}",this,_vary);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @see org.eclipse.jetty.servlets.UserAgentFilter#destroy()
|
||||
*/
|
||||
@Override
|
||||
public void destroy()
|
||||
{
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @see org.eclipse.jetty.servlets.UserAgentFilter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
|
||||
*/
|
||||
@Override
|
||||
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
|
||||
throws IOException, ServletException
|
||||
{
|
||||
HttpServletRequest request=(HttpServletRequest)req;
|
||||
HttpServletResponse response=(HttpServletResponse)res;
|
||||
|
||||
// If not a supported method or it is an Excluded URI or an excluded UA - no Vary because no matter what client, this URI is always excluded
|
||||
String requestURI = request.getRequestURI();
|
||||
if (!_methods.contains(request.getMethod()))
|
||||
{
|
||||
LOG.debug("{} excluded by method {}",this,request);
|
||||
super.doFilter(request,response,chain);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isExcludedPath(requestURI))
|
||||
{
|
||||
LOG.debug("{} excluded by path {}",this,request);
|
||||
super.doFilter(request,response,chain);
|
||||
return;
|
||||
}
|
||||
|
||||
// Exclude non compressible mime-types known from URI extension. - no Vary because no matter what client, this URI is always excluded
|
||||
if (_mimeTypes.size()>0)
|
||||
{
|
||||
String mimeType = _context.getMimeType(request.getRequestURI());
|
||||
|
||||
if (mimeType!=null && _mimeTypes.contains(mimeType)==_excludeMimeTypes)
|
||||
{
|
||||
LOG.debug("{} excluded by path suffix {}",this,request);
|
||||
// handle normally without setting vary header
|
||||
super.doFilter(request,response,chain);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (_checkGzExists && request.getServletContext()!=null)
|
||||
{
|
||||
String path=request.getServletContext().getRealPath(URIUtil.addPaths(request.getServletPath(),request.getPathInfo()));
|
||||
if (path!=null)
|
||||
{
|
||||
File gz=new File(path+".gz");
|
||||
if (gz.exists())
|
||||
{
|
||||
LOG.debug("{} gzip exists {}",this,request);
|
||||
// allow default servlet to handle
|
||||
super.doFilter(request,response,chain);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Special handling for etags
|
||||
String etag = request.getHeader("If-None-Match");
|
||||
if (etag!=null)
|
||||
{
|
||||
int dd=etag.indexOf("--");
|
||||
if (dd>0)
|
||||
request.setAttribute(ETAG,etag.substring(0,dd)+(etag.endsWith("\"")?"\"":""));
|
||||
}
|
||||
|
||||
HttpChannel<?> channel = HttpChannel.getCurrentHttpChannel();
|
||||
HttpOutput out = channel.getResponse().getHttpOutput();
|
||||
if (!(out instanceof GzipHttpOutput))
|
||||
{
|
||||
if (out.getClass()!=HttpOutput.class)
|
||||
throw new IllegalStateException();
|
||||
channel.getResponse().setHttpOutput(out = new GzipHttpOutput(channel));
|
||||
}
|
||||
|
||||
GzipHttpOutput cout=(GzipHttpOutput)out;
|
||||
|
||||
boolean exceptional=true;
|
||||
try
|
||||
{
|
||||
cout.mightCompress(this);
|
||||
super.doFilter(request,response,chain);
|
||||
exceptional=false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
LOG.debug("{} excepted {}",this,request);
|
||||
if (exceptional && !response.isCommitted())
|
||||
{
|
||||
cout.resetBuffer();
|
||||
cout.noCompression();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks to see if the userAgent is excluded
|
||||
*
|
||||
* @param ua
|
||||
* the user agent
|
||||
* @return boolean true if excluded
|
||||
*/
|
||||
private boolean isExcludedAgent(String ua)
|
||||
{
|
||||
if (ua == null)
|
||||
return false;
|
||||
|
||||
if (_excludedAgents != null)
|
||||
{
|
||||
if (_excludedAgents.contains(ua))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (_excludedAgentPatterns != null)
|
||||
{
|
||||
for (Pattern pattern : _excludedAgentPatterns)
|
||||
{
|
||||
if (pattern.matcher(ua).matches())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the path is excluded
|
||||
*
|
||||
* @param requestURI
|
||||
* the request uri
|
||||
* @return boolean true if excluded
|
||||
*/
|
||||
private boolean isExcludedPath(String requestURI)
|
||||
{
|
||||
if (requestURI == null)
|
||||
return false;
|
||||
if (_excludedPaths != null)
|
||||
{
|
||||
for (String excludedPath : _excludedPaths)
|
||||
{
|
||||
if (requestURI.startsWith(excludedPath))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_excludedPathPatterns != null)
|
||||
{
|
||||
for (Pattern pattern : _excludedPathPatterns)
|
||||
{
|
||||
if (pattern.matcher(requestURI).matches())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpField getVaryField()
|
||||
{
|
||||
return _vary;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Deflater getDeflater(Request request, long content_length)
|
||||
{
|
||||
|
||||
String ua = getUserAgent(request);
|
||||
if (ua!=null && isExcludedAgent(ua))
|
||||
{
|
||||
LOG.debug("{} excluded user agent {}",this,request);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (content_length>=0 && content_length<_minGzipSize)
|
||||
{
|
||||
LOG.debug("{} excluded minGzipSize {}",this,request);
|
||||
return null;
|
||||
}
|
||||
|
||||
String accept = request.getHttpFields().get(HttpHeader.ACCEPT_ENCODING);
|
||||
if (accept==null)
|
||||
{
|
||||
LOG.debug("{} excluded !accept {}",this,request);
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean gzip=false;
|
||||
if (GZIP.equals(accept) || accept.startsWith("gzip,"))
|
||||
gzip=true;
|
||||
else
|
||||
{
|
||||
List<String> list=HttpFields.qualityList(request.getHttpFields().getValues(HttpHeader.ACCEPT_ENCODING.asString(),","));
|
||||
for (String a:list)
|
||||
{
|
||||
if (GZIP.equalsIgnoreCase(HttpFields.valueParameters(a,null)))
|
||||
{
|
||||
gzip=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!gzip)
|
||||
{
|
||||
LOG.debug("{} excluded not gzip accept {}",this,request);
|
||||
return null;
|
||||
}
|
||||
|
||||
Deflater df = _deflater.get();
|
||||
if (df==null)
|
||||
df=new Deflater(_deflateCompressionLevel,_deflateNoWrap);
|
||||
else
|
||||
_deflater.set(null);
|
||||
|
||||
return df;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recycle(Deflater deflater)
|
||||
{
|
||||
if (_deflater.get()==null)
|
||||
_deflater.set(deflater);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExcludedMimeType(String mimetype)
|
||||
{
|
||||
return _mimeTypes.contains(mimetype) == _excludeMimeTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBufferSize()
|
||||
{
|
||||
return _bufferSize;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.util.zip.Deflater;
|
||||
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
|
||||
public interface GzipFactory
|
||||
{
|
||||
int getBufferSize();
|
||||
|
||||
HttpField getVaryField();
|
||||
|
||||
Deflater getDeflater(Request request, long content_length);
|
||||
|
||||
boolean isExcludedMimeType(String asciiToLowerCase);
|
||||
|
||||
void recycle(Deflater deflater);
|
||||
|
||||
}
|
|
@ -0,0 +1,353 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.nio.ByteBuffer;
|
||||
import java.nio.channels.WritePendingException;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.zip.CRC32;
|
||||
import java.util.zip.Deflater;
|
||||
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.eclipse.jetty.http.HttpGenerator;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.MimeTypes;
|
||||
import org.eclipse.jetty.server.HttpChannel;
|
||||
import org.eclipse.jetty.server.HttpOutput;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.IteratingNestedCallback;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
|
||||
public class GzipHttpOutput extends HttpOutput
|
||||
{
|
||||
private final static HttpGenerator.CachedHttpField CONTENT_ENCODING_GZIP=new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_ENCODING,"gzip");
|
||||
private final static byte[] GZIP_HEADER = new byte[] { (byte)0x1f, (byte)0x8b, Deflater.DEFLATED, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
private enum GZState { NOT_COMPRESSING, MIGHT_COMPRESS, COMMITTING, COMPRESSING, FINISHED};
|
||||
private final AtomicReference<GZState> _state = new AtomicReference<>(GZState.NOT_COMPRESSING);
|
||||
private final CRC32 _crc = new CRC32();
|
||||
|
||||
private Deflater _deflater;
|
||||
private GzipFactory _factory;
|
||||
private ByteBuffer _buffer;
|
||||
|
||||
|
||||
public GzipHttpOutput(HttpChannel<?> channel)
|
||||
{
|
||||
super(channel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset()
|
||||
{
|
||||
super.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void write(ByteBuffer content, boolean complete, Callback callback)
|
||||
{
|
||||
switch (_state.get())
|
||||
{
|
||||
case NOT_COMPRESSING:
|
||||
super.write(content,complete,callback);
|
||||
return;
|
||||
|
||||
case MIGHT_COMPRESS:
|
||||
commit(content,complete,callback);
|
||||
break;
|
||||
|
||||
case COMMITTING:
|
||||
throw new WritePendingException();
|
||||
|
||||
case COMPRESSING:
|
||||
gzip(content,complete,callback);
|
||||
break;
|
||||
|
||||
case FINISHED:
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
private void superWrite(ByteBuffer content, boolean complete, Callback callback)
|
||||
{
|
||||
super.write(content,complete,callback);
|
||||
}
|
||||
|
||||
private void addTrailer()
|
||||
{
|
||||
int i=_buffer.limit();
|
||||
_buffer.limit(i+8);
|
||||
|
||||
int v=(int)_crc.getValue();
|
||||
_buffer.put(i++,(byte)(v & 0xFF));
|
||||
_buffer.put(i++,(byte)((v>>>8) & 0xFF));
|
||||
_buffer.put(i++,(byte)((v>>>16) & 0xFF));
|
||||
_buffer.put(i++,(byte)((v>>>24) & 0xFF));
|
||||
|
||||
v=_deflater.getTotalIn();
|
||||
_buffer.put(i++,(byte)(v & 0xFF));
|
||||
_buffer.put(i++,(byte)((v>>>8) & 0xFF));
|
||||
_buffer.put(i++,(byte)((v>>>16) & 0xFF));
|
||||
_buffer.put(i++,(byte)((v>>>24) & 0xFF));
|
||||
}
|
||||
|
||||
|
||||
private void gzip(ByteBuffer content, boolean complete, final Callback callback)
|
||||
{
|
||||
if (content.hasRemaining())
|
||||
{
|
||||
if (content.hasArray())
|
||||
new GzipArrayCB(content,complete,callback).iterate();
|
||||
else
|
||||
new GzipBufferCB(content,complete,callback).iterate();
|
||||
}
|
||||
else if (complete)
|
||||
{
|
||||
_deflater.finish();
|
||||
|
||||
int produced=_deflater.deflate(_buffer.array(),_buffer.arrayOffset()+_buffer.limit(),_buffer.capacity()-_buffer.limit(),Deflater.NO_FLUSH);
|
||||
_buffer.limit(_buffer.limit()+produced);
|
||||
addTrailer();
|
||||
superWrite(_buffer,complete,new Callback()
|
||||
{
|
||||
@Override
|
||||
public void succeeded()
|
||||
{
|
||||
getHttpChannel().getByteBufferPool().release(_buffer);
|
||||
_buffer=null;
|
||||
callback.succeeded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Throwable x)
|
||||
{
|
||||
callback.failed(x);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected void commit(ByteBuffer content, boolean complete, Callback callback)
|
||||
{
|
||||
// Are we excluding because of status?
|
||||
Response response=getHttpChannel().getResponse();
|
||||
int sc = response.getStatus();
|
||||
if (sc>0 && (sc<200 || sc==204 || sc==205 || sc>=300))
|
||||
{
|
||||
noCompression();
|
||||
super.write(content,complete,callback);
|
||||
return;
|
||||
}
|
||||
|
||||
// Are we excluding because of mime-type?
|
||||
String ct = getHttpChannel().getResponse().getContentType();
|
||||
if (ct!=null)
|
||||
{
|
||||
ct=MimeTypes.getContentTypeWithoutCharset(ct);
|
||||
if (_factory.isExcludedMimeType(StringUtil.asciiToLowerCase(ct)))
|
||||
{
|
||||
noCompression();
|
||||
super.write(content,complete,callback);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Are we the thread that commits?
|
||||
if (_state.compareAndSet(GZState.MIGHT_COMPRESS,GZState.COMMITTING))
|
||||
{
|
||||
// We are varying the response due to accept encoding header.
|
||||
HttpFields fields = response.getHttpFields();
|
||||
fields.add(_factory.getVaryField());
|
||||
|
||||
long content_length = response.getContentLength();
|
||||
if (content_length<0 && complete)
|
||||
content_length=content.remaining();
|
||||
|
||||
_deflater = content.isDirect()?null:_factory.getDeflater(getHttpChannel().getRequest(),content_length);
|
||||
|
||||
if (_deflater==null)
|
||||
{
|
||||
_state.set(GZState.NOT_COMPRESSING);
|
||||
super.write(content,complete,callback);
|
||||
return;
|
||||
}
|
||||
|
||||
fields.put(CONTENT_ENCODING_GZIP);
|
||||
_crc.reset();
|
||||
_buffer=getHttpChannel().getByteBufferPool().acquire(_factory.getBufferSize(),false);
|
||||
BufferUtil.fill(_buffer,GZIP_HEADER,0,GZIP_HEADER.length);
|
||||
|
||||
// Adjust headers
|
||||
response.setContentLength(-1);
|
||||
String etag=fields.get(HttpHeader.ETAG);
|
||||
if (etag!=null)
|
||||
fields.put(HttpHeader.ETAG,etag.substring(0,etag.length()-1)+"--gzip\"");
|
||||
|
||||
_state.set(GZState.COMPRESSING);
|
||||
|
||||
gzip(content,complete,callback);
|
||||
}
|
||||
}
|
||||
|
||||
public void noCompression()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
switch (_state.get())
|
||||
{
|
||||
case NOT_COMPRESSING:
|
||||
return;
|
||||
|
||||
case MIGHT_COMPRESS:
|
||||
if (_state.compareAndSet(GZState.MIGHT_COMPRESS,GZState.NOT_COMPRESSING))
|
||||
return;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IllegalStateException(_state.get().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void mightCompress(GzipFactory factory)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
switch (_state.get())
|
||||
{
|
||||
case NOT_COMPRESSING:
|
||||
_factory=factory;
|
||||
if (_state.compareAndSet(GZState.NOT_COMPRESSING,GZState.MIGHT_COMPRESS))
|
||||
return;
|
||||
_factory=null;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class GzipArrayCB extends IteratingNestedCallback
|
||||
{
|
||||
private final boolean _complete;
|
||||
public GzipArrayCB(ByteBuffer content, boolean complete, Callback callback)
|
||||
{
|
||||
super(callback);
|
||||
_complete=complete;
|
||||
|
||||
byte[] array=content.array();
|
||||
int off=content.arrayOffset()+content.position();
|
||||
int len=content.remaining();
|
||||
_crc.update(array,off,len);
|
||||
_deflater.setInput(array,off,len);
|
||||
if (complete)
|
||||
_deflater.finish();
|
||||
content.position(content.limit());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected State process() throws Exception
|
||||
{
|
||||
if (_deflater.needsInput())
|
||||
{
|
||||
if (_deflater.finished())
|
||||
{
|
||||
_factory.recycle(_deflater);
|
||||
_deflater=null;
|
||||
getHttpChannel().getByteBufferPool().release(_buffer);
|
||||
_buffer=null;
|
||||
}
|
||||
return State.SUCCEEDED;
|
||||
}
|
||||
|
||||
int off=_buffer.arrayOffset()+_buffer.limit();
|
||||
int len=_buffer.capacity()-_buffer.limit()- (_complete?8:0);
|
||||
int produced=_deflater.deflate(_buffer.array(),off,len,Deflater.NO_FLUSH);
|
||||
_buffer.limit(_buffer.limit()+produced);
|
||||
boolean complete=_deflater.finished();
|
||||
if (complete)
|
||||
{
|
||||
addTrailer();
|
||||
_deflater.end(); // TODO recycle
|
||||
}
|
||||
superWrite(_buffer,complete,this);
|
||||
return State.SCHEDULED;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class GzipBufferCB extends IteratingNestedCallback
|
||||
{
|
||||
private final ByteBuffer _input;
|
||||
private final ByteBuffer _content;
|
||||
private final boolean _complete;
|
||||
public GzipBufferCB(ByteBuffer content, boolean complete, Callback callback)
|
||||
{
|
||||
super(callback);
|
||||
_input=getHttpChannel().getByteBufferPool().acquire(Math.min(_factory.getBufferSize(),content.remaining()),false);
|
||||
_content=content;
|
||||
_complete=complete;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected State process() throws Exception
|
||||
{
|
||||
if (_deflater.needsInput())
|
||||
{
|
||||
if (BufferUtil.isEmpty(_content))
|
||||
{
|
||||
if (_deflater.finished())
|
||||
{
|
||||
_factory.recycle(_deflater);
|
||||
_deflater=null;
|
||||
getHttpChannel().getByteBufferPool().release(_buffer);
|
||||
_buffer=null;
|
||||
}
|
||||
return State.SUCCEEDED;
|
||||
}
|
||||
|
||||
BufferUtil.clearToFill(_input);
|
||||
BufferUtil.put(_content,_input);
|
||||
BufferUtil.flipToFlush(_input,0);
|
||||
|
||||
byte[] array=_input.array();
|
||||
int off=_input.arrayOffset()+_input.position();
|
||||
int len=_input.remaining();
|
||||
_crc.update(array,off,len);
|
||||
_deflater.setInput(array,off,len);
|
||||
if (_complete && BufferUtil.isEmpty(_content))
|
||||
_deflater.finish();
|
||||
}
|
||||
|
||||
int off=_buffer.arrayOffset()+_buffer.limit();
|
||||
int len=_buffer.capacity()-_buffer.limit() - (_complete?8:0);
|
||||
int produced=_deflater.deflate(_buffer.array(),off,len,Deflater.NO_FLUSH);
|
||||
_buffer.limit(_buffer.limit()+produced);
|
||||
boolean complete=_deflater.finished();
|
||||
if (complete)
|
||||
addTrailer();
|
||||
superWrite(_buffer,complete,this);
|
||||
return State.SCHEDULED;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -22,13 +22,15 @@ import java.io.File;
|
|||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.Servlet;
|
||||
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.HttpTester;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.servlet.FilterHolder;
|
||||
import org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper;
|
||||
import org.eclipse.jetty.servlets.gzip.GzipTester;
|
||||
import org.eclipse.jetty.servlets.gzip.TestServletBufferTypeLengthWrite;
|
||||
import org.eclipse.jetty.servlets.gzip.TestServletLengthStreamTypeWrite;
|
||||
import org.eclipse.jetty.servlets.gzip.TestServletLengthTypeStreamWrite;
|
||||
import org.eclipse.jetty.servlets.gzip.TestServletStreamLengthTypeWrite;
|
||||
|
@ -71,32 +73,45 @@ public class GzipFilterContentLengthTest
|
|||
{
|
||||
return Arrays.asList(new Object[][]
|
||||
{
|
||||
{ TestServletLengthStreamTypeWrite.class, GzipFilter.GZIP },
|
||||
{ TestServletLengthTypeStreamWrite.class, GzipFilter.GZIP },
|
||||
{ TestServletStreamLengthTypeWrite.class, GzipFilter.GZIP },
|
||||
{ TestServletStreamLengthTypeWriteWithFlush.class, GzipFilter.GZIP },
|
||||
{ TestServletStreamTypeLengthWrite.class, GzipFilter.GZIP },
|
||||
{ TestServletTypeLengthStreamWrite.class, GzipFilter.GZIP },
|
||||
{ TestServletTypeStreamLengthWrite.class, GzipFilter.GZIP },
|
||||
{ TestServletLengthStreamTypeWrite.class, GzipFilter.DEFLATE },
|
||||
{ TestServletLengthTypeStreamWrite.class, GzipFilter.DEFLATE },
|
||||
{ TestServletStreamLengthTypeWrite.class, GzipFilter.DEFLATE },
|
||||
{ TestServletStreamLengthTypeWriteWithFlush.class, GzipFilter.DEFLATE },
|
||||
{ TestServletStreamTypeLengthWrite.class, GzipFilter.DEFLATE },
|
||||
{ TestServletTypeLengthStreamWrite.class, GzipFilter.DEFLATE },
|
||||
{ TestServletTypeStreamLengthWrite.class, GzipFilter.DEFLATE }
|
||||
{ AsyncGzipFilter.class, TestServletLengthStreamTypeWrite.class, GzipFilter.GZIP },
|
||||
{ AsyncGzipFilter.class, TestServletLengthTypeStreamWrite.class, GzipFilter.GZIP },
|
||||
{ AsyncGzipFilter.class, TestServletStreamLengthTypeWrite.class, GzipFilter.GZIP },
|
||||
{ AsyncGzipFilter.class, TestServletStreamLengthTypeWriteWithFlush.class, GzipFilter.GZIP },
|
||||
{ AsyncGzipFilter.class, TestServletStreamTypeLengthWrite.class, GzipFilter.GZIP },
|
||||
{ AsyncGzipFilter.class, TestServletTypeLengthStreamWrite.class, GzipFilter.GZIP },
|
||||
{ AsyncGzipFilter.class, TestServletTypeStreamLengthWrite.class, GzipFilter.GZIP },
|
||||
{ AsyncGzipFilter.class, TestServletBufferTypeLengthWrite.class, GzipFilter.GZIP },
|
||||
|
||||
{ GzipFilter.class, TestServletLengthStreamTypeWrite.class, GzipFilter.GZIP },
|
||||
{ GzipFilter.class, TestServletLengthTypeStreamWrite.class, GzipFilter.GZIP },
|
||||
{ GzipFilter.class, TestServletStreamLengthTypeWrite.class, GzipFilter.GZIP },
|
||||
{ GzipFilter.class, TestServletStreamLengthTypeWriteWithFlush.class, GzipFilter.GZIP },
|
||||
{ GzipFilter.class, TestServletStreamTypeLengthWrite.class, GzipFilter.GZIP },
|
||||
{ GzipFilter.class, TestServletTypeLengthStreamWrite.class, GzipFilter.GZIP },
|
||||
{ GzipFilter.class, TestServletTypeStreamLengthWrite.class, GzipFilter.GZIP },
|
||||
|
||||
{ GzipFilter.class, TestServletLengthStreamTypeWrite.class, GzipFilter.DEFLATE },
|
||||
{ GzipFilter.class, TestServletLengthTypeStreamWrite.class, GzipFilter.DEFLATE },
|
||||
{ GzipFilter.class, TestServletStreamLengthTypeWrite.class, GzipFilter.DEFLATE },
|
||||
{ GzipFilter.class, TestServletStreamLengthTypeWriteWithFlush.class, GzipFilter.DEFLATE },
|
||||
{ GzipFilter.class, TestServletStreamTypeLengthWrite.class, GzipFilter.DEFLATE },
|
||||
{ GzipFilter.class, TestServletTypeLengthStreamWrite.class, GzipFilter.DEFLATE },
|
||||
{ GzipFilter.class, TestServletTypeStreamLengthWrite.class, GzipFilter.DEFLATE },
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private static final int LARGE = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 8;
|
||||
private static final int MEDIUM = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE;
|
||||
private static final int SMALL = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE / 4;
|
||||
private static final int TINY = CompressedResponseWrapper.DEFAULT_MIN_COMPRESS_SIZE/ 2;
|
||||
private static final HttpConfiguration defaultHttp = new HttpConfiguration();
|
||||
private static final int LARGE = defaultHttp.getOutputBufferSize() * 8;
|
||||
private static final int MEDIUM = defaultHttp.getOutputBufferSize();
|
||||
private static final int SMALL = defaultHttp.getOutputBufferSize() / 4;
|
||||
private static final int TINY = AsyncGzipFilter.DEFAULT_MIN_GZIP_SIZE / 2;
|
||||
|
||||
private String compressionType;
|
||||
|
||||
public GzipFilterContentLengthTest(Class<? extends Servlet> testServlet, String compressionType)
|
||||
public GzipFilterContentLengthTest(Class<? extends Filter> testFilter,Class<? extends Servlet> testServlet, String compressionType)
|
||||
{
|
||||
this.testFilter = testFilter;
|
||||
this.testServlet = testServlet;
|
||||
this.compressionType = compressionType;
|
||||
}
|
||||
|
@ -104,11 +119,13 @@ public class GzipFilterContentLengthTest
|
|||
@Rule
|
||||
public TestingDir testingdir = new TestingDir();
|
||||
|
||||
private Class<? extends Filter> testFilter;
|
||||
private Class<? extends Servlet> testServlet;
|
||||
|
||||
private void assertIsGzipCompressed(String filename, int filesize) throws Exception
|
||||
{
|
||||
GzipTester tester = new GzipTester(testingdir, compressionType);
|
||||
tester.setGzipFilterClass(testFilter);
|
||||
|
||||
File testfile = tester.prepareServerFile(testServlet.getSimpleName() + "-" + filename,filesize);
|
||||
|
||||
|
@ -129,6 +146,7 @@ public class GzipFilterContentLengthTest
|
|||
private void assertIsNotGzipCompressed(String filename, int filesize) throws Exception
|
||||
{
|
||||
GzipTester tester = new GzipTester(testingdir, compressionType);
|
||||
tester.setGzipFilterClass(testFilter);
|
||||
|
||||
File testfile = tester.prepareServerFile(testServlet.getSimpleName() + "-" + filename,filesize);
|
||||
|
||||
|
|
|
@ -23,6 +23,8 @@ import java.io.IOException;
|
|||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
|
||||
import org.eclipse.jetty.servlet.DefaultServlet;
|
||||
import org.eclipse.jetty.servlet.FilterHolder;
|
||||
import org.eclipse.jetty.servlets.gzip.GzipTester;
|
||||
|
@ -49,56 +51,61 @@ public class GzipFilterDefaultNoRecompressTest
|
|||
return Arrays.asList(new Object[][]
|
||||
{
|
||||
// Some already compressed files
|
||||
{ "test_quotes.gz", "application/gzip", GzipFilter.GZIP },
|
||||
{ "test_quotes.bz2", "application/bzip2", GzipFilter.GZIP },
|
||||
{ "test_quotes.zip", "application/zip", GzipFilter.GZIP },
|
||||
{ "test_quotes.rar", "application/octet-stream", GzipFilter.GZIP },
|
||||
{ GzipFilter.class, "test_quotes.gz", "application/gzip", GzipFilter.GZIP },
|
||||
{ GzipFilter.class, "test_quotes.bz2", "application/bzip2", GzipFilter.GZIP },
|
||||
{ GzipFilter.class, "test_quotes.zip", "application/zip", GzipFilter.GZIP },
|
||||
{ GzipFilter.class, "test_quotes.rar", "application/octet-stream", GzipFilter.GZIP },
|
||||
// Some images (common first)
|
||||
{ "jetty_logo.png", "image/png", GzipFilter.GZIP },
|
||||
{ "jetty_logo.gif", "image/gif", GzipFilter.GZIP },
|
||||
{ "jetty_logo.jpeg", "image/jpeg", GzipFilter.GZIP },
|
||||
{ "jetty_logo.jpg", "image/jpeg", GzipFilter.GZIP },
|
||||
{ GzipFilter.class, "jetty_logo.png", "image/png", GzipFilter.GZIP },
|
||||
{ GzipFilter.class, "jetty_logo.gif", "image/gif", GzipFilter.GZIP },
|
||||
{ GzipFilter.class, "jetty_logo.jpeg", "image/jpeg", GzipFilter.GZIP },
|
||||
{ GzipFilter.class, "jetty_logo.jpg", "image/jpeg", GzipFilter.GZIP },
|
||||
// Lesser encountered images (usually found being requested from non-browser clients)
|
||||
{ "jetty_logo.bmp", "image/bmp", GzipFilter.GZIP },
|
||||
{ "jetty_logo.tga", "application/tga", GzipFilter.GZIP },
|
||||
{ "jetty_logo.tif", "image/tiff", GzipFilter.GZIP },
|
||||
{ "jetty_logo.tiff", "image/tiff", GzipFilter.GZIP },
|
||||
{ "jetty_logo.xcf", "image/xcf", GzipFilter.GZIP },
|
||||
{ "jetty_logo.jp2", "image/jpeg2000", GzipFilter.GZIP },
|
||||
{ GzipFilter.class, "jetty_logo.bmp", "image/bmp", GzipFilter.GZIP },
|
||||
{ GzipFilter.class, "jetty_logo.tga", "application/tga", GzipFilter.GZIP },
|
||||
{ GzipFilter.class, "jetty_logo.tif", "image/tiff", GzipFilter.GZIP },
|
||||
{ GzipFilter.class, "jetty_logo.tiff", "image/tiff", GzipFilter.GZIP },
|
||||
{ GzipFilter.class, "jetty_logo.xcf", "image/xcf", GzipFilter.GZIP },
|
||||
{ GzipFilter.class, "jetty_logo.jp2", "image/jpeg2000", GzipFilter.GZIP },
|
||||
//qvalue disables compression
|
||||
{ "test_quotes.txt", "text/plain", GzipFilter.GZIP+";q=0"},
|
||||
{ "test_quotes.txt", "text/plain", GzipFilter.GZIP+"; q = 0 "},
|
||||
|
||||
|
||||
// Same tests again for deflate
|
||||
{ GzipFilter.class, "test_quotes.txt", "text/plain", GzipFilter.GZIP+";q=0"},
|
||||
{ GzipFilter.class, "test_quotes.txt", "text/plain", GzipFilter.GZIP+"; q = 0 "},
|
||||
|
||||
|
||||
// Some already compressed files
|
||||
{ "test_quotes.gz", "application/gzip", GzipFilter.DEFLATE },
|
||||
{ "test_quotes.bz2", "application/bzip2", GzipFilter.DEFLATE },
|
||||
{ "test_quotes.zip", "application/zip", GzipFilter.DEFLATE },
|
||||
{ "test_quotes.rar", "application/octet-stream", GzipFilter.DEFLATE },
|
||||
{ AsyncGzipFilter.class, "test_quotes.gz", "application/gzip", GzipFilter.GZIP },
|
||||
{ AsyncGzipFilter.class, "test_quotes.bz2", "application/bzip2", GzipFilter.GZIP },
|
||||
{ AsyncGzipFilter.class, "test_quotes.zip", "application/zip", GzipFilter.GZIP },
|
||||
{ AsyncGzipFilter.class, "test_quotes.rar", "application/octet-stream", GzipFilter.GZIP },
|
||||
// Some images (common first)
|
||||
{ "jetty_logo.png", "image/png", GzipFilter.DEFLATE },
|
||||
{ "jetty_logo.gif", "image/gif", GzipFilter.DEFLATE },
|
||||
{ "jetty_logo.jpeg", "image/jpeg", GzipFilter.DEFLATE },
|
||||
{ "jetty_logo.jpg", "image/jpeg", GzipFilter.DEFLATE },
|
||||
{ AsyncGzipFilter.class, "jetty_logo.png", "image/png", GzipFilter.GZIP },
|
||||
{ AsyncGzipFilter.class, "jetty_logo.gif", "image/gif", GzipFilter.GZIP },
|
||||
{ AsyncGzipFilter.class, "jetty_logo.jpeg", "image/jpeg", GzipFilter.GZIP },
|
||||
{ AsyncGzipFilter.class, "jetty_logo.jpg", "image/jpeg", GzipFilter.GZIP },
|
||||
// Lesser encountered images (usually found being requested from non-browser clients)
|
||||
{ "jetty_logo.bmp", "image/bmp", GzipFilter.DEFLATE },
|
||||
{ "jetty_logo.tga", "application/tga", GzipFilter.DEFLATE },
|
||||
{ "jetty_logo.tif", "image/tiff", GzipFilter.DEFLATE },
|
||||
{ "jetty_logo.tiff", "image/tiff", GzipFilter.DEFLATE },
|
||||
{ "jetty_logo.xcf", "image/xcf", GzipFilter.DEFLATE },
|
||||
{ "jetty_logo.jp2", "image/jpeg2000", GzipFilter.DEFLATE } });
|
||||
{ AsyncGzipFilter.class, "jetty_logo.bmp", "image/bmp", GzipFilter.GZIP },
|
||||
{ AsyncGzipFilter.class, "jetty_logo.tga", "application/tga", GzipFilter.GZIP },
|
||||
{ AsyncGzipFilter.class, "jetty_logo.tif", "image/tiff", GzipFilter.GZIP },
|
||||
{ AsyncGzipFilter.class, "jetty_logo.tiff", "image/tiff", GzipFilter.GZIP },
|
||||
{ AsyncGzipFilter.class, "jetty_logo.xcf", "image/xcf", GzipFilter.GZIP },
|
||||
{ AsyncGzipFilter.class, "jetty_logo.jp2", "image/jpeg2000", GzipFilter.GZIP },
|
||||
//qvalue disables compression
|
||||
{ AsyncGzipFilter.class, "test_quotes.txt", "text/plain", GzipFilter.GZIP+";q=0"},
|
||||
{ AsyncGzipFilter.class, "test_quotes.txt", "text/plain", GzipFilter.GZIP+"; q = 0 "}
|
||||
});
|
||||
}
|
||||
|
||||
@Rule
|
||||
public TestingDir testingdir = new TestingDir();
|
||||
|
||||
private Class<? extends Filter> testFilter;
|
||||
private String alreadyCompressedFilename;
|
||||
private String expectedContentType;
|
||||
private String compressionType;
|
||||
|
||||
public GzipFilterDefaultNoRecompressTest(String testFilename, String expectedContentType, String compressionType)
|
||||
public GzipFilterDefaultNoRecompressTest(Class<? extends Filter> testFilter,String testFilename, String expectedContentType, String compressionType)
|
||||
{
|
||||
this.testFilter = testFilter;
|
||||
this.alreadyCompressedFilename = testFilename;
|
||||
this.expectedContentType = expectedContentType;
|
||||
this.compressionType = compressionType;
|
||||
|
@ -108,6 +115,7 @@ public class GzipFilterDefaultNoRecompressTest
|
|||
public void testNotGzipFiltered_Default_AlreadyCompressed() throws Exception
|
||||
{
|
||||
GzipTester tester = new GzipTester(testingdir, compressionType);
|
||||
tester.setGzipFilterClass(testFilter);
|
||||
|
||||
copyTestFileToServer(alreadyCompressedFilename);
|
||||
|
||||
|
|
|
@ -21,22 +21,22 @@ package org.eclipse.jetty.servlets;
|
|||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.HttpTester;
|
||||
import org.eclipse.jetty.servlet.DefaultServlet;
|
||||
import org.eclipse.jetty.servlet.FilterHolder;
|
||||
import org.eclipse.jetty.servlets.gzip.CompressedResponseWrapper;
|
||||
import org.eclipse.jetty.servlets.gzip.GzipTester;
|
||||
import org.eclipse.jetty.toolchain.test.TestingDir;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
@ -50,20 +50,22 @@ import org.junit.runners.Parameterized.Parameters;
|
|||
public class GzipFilterDefaultTest
|
||||
{
|
||||
@Parameters
|
||||
public static Collection<String[]> data()
|
||||
public static List<Object[]> data()
|
||||
{
|
||||
String[][] data = new String[][]
|
||||
{
|
||||
{ GzipFilter.GZIP },
|
||||
{ GzipFilter.DEFLATE } };
|
||||
|
||||
return Arrays.asList(data);
|
||||
return Arrays.asList(new Object[][]
|
||||
{
|
||||
{ AsyncGzipFilter.class, GzipFilter.GZIP },
|
||||
{ GzipFilter.class, GzipFilter.GZIP },
|
||||
{ GzipFilter.class, GzipFilter.DEFLATE },
|
||||
});
|
||||
}
|
||||
|
||||
private Class<? extends Filter> testFilter;
|
||||
private String compressionType;
|
||||
|
||||
public GzipFilterDefaultTest(String compressionType)
|
||||
public GzipFilterDefaultTest(Class<? extends Filter> testFilter, String compressionType)
|
||||
{
|
||||
this.testFilter=testFilter;
|
||||
this.compressionType = compressionType;
|
||||
}
|
||||
|
||||
|
@ -105,7 +107,10 @@ public class GzipFilterDefaultTest
|
|||
|
||||
public static class HttpContentTypeWithEncoding extends HttpServlet
|
||||
{
|
||||
public static final String COMPRESSED_CONTENT = "<html><head></head><body><h1>COMPRESSED</h1></body></html>";
|
||||
public static final String COMPRESSED_CONTENT = "<html><head></head><body><h1>COMPRESSABLE CONTENT</h1>"+
|
||||
"This content must be longer than the default min gzip length, which is 256 bytes. "+
|
||||
"The moon is blue to a fish in love. How now brown cow. The quick brown fox jumped over the lazy dog. A woman needs a man like a fish needs a bicycle!"+
|
||||
"</body></html>";
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
|
||||
|
@ -128,9 +133,9 @@ public class GzipFilterDefaultTest
|
|||
public void testIsGzipByMethod() throws Exception
|
||||
{
|
||||
GzipTester tester = new GzipTester(testingdir, compressionType);
|
||||
tester.setGzipFilterClass(testFilter);
|
||||
|
||||
// Test content that is smaller than the buffer.
|
||||
int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 2;
|
||||
int filesize = tester.getOutputBufferSize() * 2;
|
||||
tester.prepareServerFile("file.txt",filesize);
|
||||
|
||||
FilterHolder holder = tester.setContentServlet(GetServlet.class);
|
||||
|
@ -177,8 +182,8 @@ public class GzipFilterDefaultTest
|
|||
public void testIsGzipCompressedEmpty() throws Exception
|
||||
{
|
||||
GzipTester tester = new GzipTester(testingdir, compressionType);
|
||||
tester.setGzipFilterClass(testFilter);
|
||||
|
||||
// Test content that is smaller than the buffer.
|
||||
tester.prepareServerFile("empty.txt",0);
|
||||
|
||||
FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
|
||||
|
@ -199,9 +204,9 @@ public class GzipFilterDefaultTest
|
|||
public void testIsGzipCompressedTiny() throws Exception
|
||||
{
|
||||
GzipTester tester = new GzipTester(testingdir, compressionType);
|
||||
tester.setGzipFilterClass(testFilter);
|
||||
|
||||
// Test content that is smaller than the buffer.
|
||||
int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE / 4;
|
||||
int filesize = tester.getOutputBufferSize() / 4;
|
||||
tester.prepareServerFile("file.txt",filesize);
|
||||
|
||||
FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
|
||||
|
@ -218,62 +223,14 @@ public class GzipFilterDefaultTest
|
|||
tester.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsGzipCompressedTinyWithQ() throws Exception
|
||||
{
|
||||
GzipTester tester = new GzipTester(testingdir, compressionType+";q=0.5");
|
||||
|
||||
// Test content that is smaller than the buffer.
|
||||
int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE / 4;
|
||||
tester.prepareServerFile("file.txt",filesize);
|
||||
|
||||
FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
|
||||
holder.setInitParameter("mimeTypes","text/plain");
|
||||
|
||||
try
|
||||
{
|
||||
tester.start();
|
||||
HttpTester.Response http = tester.assertIsResponseGzipCompressed("GET","file.txt");
|
||||
Assert.assertEquals("Accept-Encoding",http.get("Vary"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
tester.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsGzipCompressedTinyWithBadQ() throws Exception
|
||||
{
|
||||
GzipTester tester = new GzipTester(testingdir, compressionType+";q=");
|
||||
|
||||
// Test content that is smaller than the buffer.
|
||||
int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE / 4;
|
||||
tester.prepareServerFile("file.txt",filesize);
|
||||
|
||||
FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
|
||||
holder.setInitParameter("mimeTypes","text/plain");
|
||||
|
||||
try
|
||||
{
|
||||
tester.start();
|
||||
HttpTester.Response http = tester.assertIsResponseGzipCompressed("GET","file.txt");
|
||||
Assert.assertEquals("Accept-Encoding",http.get("Vary"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
tester.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsGzipCompressedLarge() throws Exception
|
||||
{
|
||||
GzipTester tester = new GzipTester(testingdir, compressionType);
|
||||
tester.setGzipFilterClass(testFilter);
|
||||
|
||||
// Test content that is smaller than the buffer.
|
||||
int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4;
|
||||
int filesize = tester.getOutputBufferSize() * 4;
|
||||
tester.prepareServerFile("file.txt",filesize);
|
||||
|
||||
FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
|
||||
|
@ -296,9 +253,9 @@ public class GzipFilterDefaultTest
|
|||
public void testGzipedIfModified() throws Exception
|
||||
{
|
||||
GzipTester tester = new GzipTester(testingdir, compressionType);
|
||||
tester.setGzipFilterClass(testFilter);
|
||||
|
||||
// Test content that is smaller than the buffer.
|
||||
int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4;
|
||||
int filesize = tester.getOutputBufferSize() * 4;
|
||||
tester.prepareServerFile("file.txt",filesize);
|
||||
|
||||
FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
|
||||
|
@ -321,9 +278,9 @@ public class GzipFilterDefaultTest
|
|||
public void testNotGzipedIfNotModified() throws Exception
|
||||
{
|
||||
GzipTester tester = new GzipTester(testingdir, compressionType);
|
||||
tester.setGzipFilterClass(testFilter);
|
||||
|
||||
// Test content that is smaller than the buffer.
|
||||
int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4;
|
||||
int filesize = tester.getOutputBufferSize() * 4;
|
||||
tester.prepareServerFile("file.txt",filesize);
|
||||
|
||||
FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
|
||||
|
@ -343,11 +300,12 @@ public class GzipFilterDefaultTest
|
|||
|
||||
|
||||
@Test
|
||||
public void testIsNotGzipCompressedWithQ() throws Exception
|
||||
public void testIsNotGzipCompressedWithZeroQ() throws Exception
|
||||
{
|
||||
GzipTester tester = new GzipTester(testingdir, compressionType+"; q = 0");
|
||||
GzipTester tester = new GzipTester(testingdir, compressionType+"; q=0");
|
||||
tester.setGzipFilterClass(testFilter);
|
||||
|
||||
int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE / 4;
|
||||
int filesize = tester.getOutputBufferSize() / 4;
|
||||
tester.prepareServerFile("file.txt",filesize);
|
||||
|
||||
FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
|
||||
|
@ -364,13 +322,38 @@ public class GzipFilterDefaultTest
|
|||
tester.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsGzipCompressedWithQ() throws Exception
|
||||
{
|
||||
GzipTester tester = new GzipTester(testingdir, compressionType,"something;q=0.1,"+compressionType+";q=0.5");
|
||||
tester.setGzipFilterClass(testFilter);
|
||||
|
||||
int filesize = tester.getOutputBufferSize() / 4;
|
||||
tester.prepareServerFile("file.txt",filesize);
|
||||
|
||||
FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
|
||||
holder.setInitParameter("mimeTypes","text/plain");
|
||||
|
||||
try
|
||||
{
|
||||
tester.start();
|
||||
HttpTester.Response http = tester.assertIsResponseGzipCompressed("GET","file.txt");
|
||||
Assert.assertEquals("Accept-Encoding",http.get("Vary"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
tester.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsNotGzipCompressedByContentType() throws Exception
|
||||
{
|
||||
GzipTester tester = new GzipTester(testingdir, compressionType);
|
||||
tester.setGzipFilterClass(testFilter);
|
||||
|
||||
int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4;
|
||||
int filesize = tester.getOutputBufferSize() * 4;
|
||||
tester.prepareServerFile("file.mp3",filesize);
|
||||
|
||||
FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
|
||||
|
@ -392,6 +375,7 @@ public class GzipFilterDefaultTest
|
|||
public void testGzipCompressedByContentTypeWithEncoding() throws Exception
|
||||
{
|
||||
GzipTester tester = new GzipTester(testingdir, compressionType);
|
||||
tester.setGzipFilterClass(testFilter);
|
||||
FilterHolder holder = tester.setContentServlet(HttpContentTypeWithEncoding.class);
|
||||
holder.setInitParameter("mimeTypes","text/plain");
|
||||
try
|
||||
|
@ -399,8 +383,6 @@ public class GzipFilterDefaultTest
|
|||
tester.start();
|
||||
HttpTester.Response http = tester.assertNonStaticContentIsResponseGzipCompressed("GET","xxx", HttpContentTypeWithEncoding.COMPRESSED_CONTENT);
|
||||
Assert.assertEquals("Accept-Encoding",http.get("Vary"));
|
||||
System.err.println(http.get("Content-Type"));
|
||||
System.err.println(http.get("Content-Encoding"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -413,8 +395,9 @@ public class GzipFilterDefaultTest
|
|||
public void testIsNotGzipCompressedByDeferredContentType() throws Exception
|
||||
{
|
||||
GzipTester tester = new GzipTester(testingdir, compressionType);
|
||||
tester.setGzipFilterClass(testFilter);
|
||||
|
||||
int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4;
|
||||
int filesize = tester.getOutputBufferSize() * 4;
|
||||
tester.prepareServerFile("file.mp3.deferred",filesize);
|
||||
|
||||
FilterHolder holder = tester.setContentServlet(GetServlet.class);
|
||||
|
@ -436,6 +419,7 @@ public class GzipFilterDefaultTest
|
|||
public void testIsNotGzipCompressedHttpStatus() throws Exception
|
||||
{
|
||||
GzipTester tester = new GzipTester(testingdir, compressionType);
|
||||
tester.setGzipFilterClass(testFilter);
|
||||
|
||||
// Test error code 204
|
||||
FilterHolder holder = tester.setContentServlet(HttpStatusServlet.class);
|
||||
|
@ -457,6 +441,7 @@ public class GzipFilterDefaultTest
|
|||
public void testIsNotGzipCompressedHttpBadRequestStatus() throws Exception
|
||||
{
|
||||
GzipTester tester = new GzipTester(testingdir, compressionType);
|
||||
tester.setGzipFilterClass(testFilter);
|
||||
|
||||
// Test error code 400
|
||||
FilterHolder holder = tester.setContentServlet(HttpErrorServlet.class);
|
||||
|
@ -478,12 +463,13 @@ public class GzipFilterDefaultTest
|
|||
public void testUserAgentExclusion() throws Exception
|
||||
{
|
||||
GzipTester tester = new GzipTester(testingdir,compressionType);
|
||||
tester.setGzipFilterClass(testFilter);
|
||||
|
||||
FilterHolder holder = tester.setContentServlet(DefaultServlet.class);
|
||||
holder.setInitParameter("excludedAgents","bar, foo");
|
||||
tester.setUserAgent("foo");
|
||||
|
||||
int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4;
|
||||
int filesize = tester.getOutputBufferSize() * 4;
|
||||
tester.prepareServerFile("file.txt",filesize);
|
||||
|
||||
try
|
||||
|
@ -501,13 +487,14 @@ public class GzipFilterDefaultTest
|
|||
public void testUserAgentExclusionByExcludedAgentPatterns() throws Exception
|
||||
{
|
||||
GzipTester tester = new GzipTester(testingdir,compressionType);
|
||||
tester.setGzipFilterClass(testFilter);
|
||||
|
||||
FilterHolder holder = tester.setContentServlet(DefaultServlet.class);
|
||||
holder.setInitParameter("excludedAgents","bar");
|
||||
holder.setInitParameter("excludeAgentPatterns","fo.*");
|
||||
tester.setUserAgent("foo");
|
||||
|
||||
int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4;
|
||||
int filesize = tester.getOutputBufferSize() * 4;
|
||||
tester.prepareServerFile("file.txt",filesize);
|
||||
|
||||
try
|
||||
|
@ -525,11 +512,12 @@ public class GzipFilterDefaultTest
|
|||
public void testExcludePaths() throws Exception
|
||||
{
|
||||
GzipTester tester = new GzipTester(testingdir,compressionType);
|
||||
tester.setGzipFilterClass(testFilter);
|
||||
|
||||
FilterHolder holder = tester.setContentServlet(DefaultServlet.class);
|
||||
holder.setInitParameter("excludePaths","/bar/, /context/");
|
||||
|
||||
int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4;
|
||||
int filesize = tester.getOutputBufferSize() * 4;
|
||||
tester.prepareServerFile("file.txt",filesize);
|
||||
|
||||
try
|
||||
|
@ -547,11 +535,12 @@ public class GzipFilterDefaultTest
|
|||
public void testExcludePathPatterns() throws Exception
|
||||
{
|
||||
GzipTester tester = new GzipTester(testingdir,compressionType);
|
||||
tester.setGzipFilterClass(testFilter);
|
||||
|
||||
FilterHolder holder = tester.setContentServlet(DefaultServlet.class);
|
||||
holder.setInitParameter("excludePathPatterns","/cont.*");
|
||||
|
||||
int filesize = CompressedResponseWrapper.DEFAULT_BUFFER_SIZE * 4;
|
||||
int filesize = tester.getOutputBufferSize() * 4;
|
||||
tester.prepareServerFile("file.txt",filesize);
|
||||
|
||||
try
|
||||
|
|
|
@ -44,12 +44,14 @@ import java.util.zip.Inflater;
|
|||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.Servlet;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.DateGenerator;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpTester;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.servlet.FilterHolder;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.eclipse.jetty.servlet.ServletTester;
|
||||
|
@ -62,21 +64,33 @@ import org.junit.Assert;
|
|||
|
||||
public class GzipTester
|
||||
{
|
||||
private Class<? extends GzipFilter> gzipFilterClass = GzipFilter.class;
|
||||
private Class<? extends Filter> gzipFilterClass = GzipFilter.class;
|
||||
private String encoding = "ISO8859_1";
|
||||
private String userAgent = null;
|
||||
private ServletTester tester;
|
||||
private final ServletTester tester = new ServletTester();;
|
||||
private TestingDir testdir;
|
||||
private String accept;
|
||||
private String compressionType;
|
||||
|
||||
public GzipTester(TestingDir testingdir, String compressionType, String accept)
|
||||
{
|
||||
this.testdir = testingdir;
|
||||
this.compressionType = compressionType;
|
||||
this.accept=accept;
|
||||
}
|
||||
|
||||
public GzipTester(TestingDir testingdir, String compressionType)
|
||||
{
|
||||
this.testdir = testingdir;
|
||||
this.compressionType = compressionType;
|
||||
// Make sure we start with a clean testing directory.
|
||||
// DOES NOT WORK IN WINDOWS - this.testdir.ensureEmpty();
|
||||
this.accept=compressionType;
|
||||
}
|
||||
|
||||
public int getOutputBufferSize()
|
||||
{
|
||||
return tester.getConnector().getConnectionFactory(HttpConnectionFactory.class).getHttpConfiguration().getOutputBufferSize();
|
||||
}
|
||||
|
||||
public HttpTester.Response assertIsResponseGzipCompressed(String method, String filename) throws Exception
|
||||
{
|
||||
return assertIsResponseGzipCompressed(method,filename,filename,-1);
|
||||
|
@ -101,7 +115,7 @@ public class GzipTester
|
|||
request.setMethod(method);
|
||||
request.setVersion("HTTP/1.0");
|
||||
request.setHeader("Host","tester");
|
||||
request.setHeader("Accept-Encoding",compressionType);
|
||||
request.setHeader("Accept-Encoding",accept);
|
||||
|
||||
if (this.userAgent != null)
|
||||
request.setHeader("User-Agent", this.userAgent);
|
||||
|
@ -569,7 +583,6 @@ public class GzipTester
|
|||
*/
|
||||
public FilterHolder setContentServlet(Class<? extends Servlet> servletClass) throws IOException
|
||||
{
|
||||
tester = new ServletTester();
|
||||
tester.setContextPath("/context");
|
||||
tester.setResourceBase(testdir.getDir().getCanonicalPath());
|
||||
ServletHolder servletHolder = tester.addServlet(servletClass,"/");
|
||||
|
@ -580,12 +593,12 @@ public class GzipTester
|
|||
return holder;
|
||||
}
|
||||
|
||||
public Class<? extends GzipFilter> getGzipFilterClass()
|
||||
public Class<? extends Filter> getGzipFilterClass()
|
||||
{
|
||||
return gzipFilterClass;
|
||||
}
|
||||
|
||||
public void setGzipFilterClass(Class<? extends GzipFilter> gzipFilterClass)
|
||||
public void setGzipFilterClass(Class<? extends Filter> gzipFilterClass)
|
||||
{
|
||||
this.gzipFilterClass = gzipFilterClass;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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.nio.ByteBuffer;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.server.HttpOutput;
|
||||
import org.eclipse.jetty.servlets.GzipFilter;
|
||||
|
||||
/**
|
||||
* A sample servlet to serve static content, using a order of construction that has caused problems for
|
||||
* {@link GzipFilter} in the past.
|
||||
*
|
||||
* Using a real-world pattern of:
|
||||
*
|
||||
* <pre>
|
||||
* 1) get stream
|
||||
* 2) set content type
|
||||
* 2) set content length
|
||||
* 4) write
|
||||
* </pre>
|
||||
*
|
||||
* @see <a href="Eclipse Bug 354014">http://bugs.eclipse.org/354014</a>
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class TestServletBufferTypeLengthWrite extends TestDirContentServlet
|
||||
{
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
String fileName = request.getServletPath();
|
||||
byte[] dataBytes = loadContentFileBytes(fileName);
|
||||
|
||||
ServletOutputStream out = response.getOutputStream();
|
||||
|
||||
if (fileName.endsWith("txt"))
|
||||
response.setContentType("text/plain");
|
||||
else if (fileName.endsWith("mp3"))
|
||||
response.setContentType("audio/mpeg");
|
||||
response.setHeader("ETag","W/etag-"+fileName);
|
||||
|
||||
response.setContentLength(dataBytes.length);
|
||||
|
||||
((HttpOutput)out).write(ByteBuffer.wrap(dataBytes).asReadOnlyBuffer());
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
||||
#org.eclipse.jetty.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.servlets.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.servlets.GzipFilter.LEVEL=DEBUG
|
||||
|
|
|
@ -89,6 +89,7 @@ public abstract class AbstractHTTPSPDYTest
|
|||
server.addConnector(connector);
|
||||
server.setHandler(handler);
|
||||
server.start();
|
||||
server.dumpStdErr();
|
||||
return new InetSocketAddress("localhost", connector.getLocalPort());
|
||||
}
|
||||
|
||||
|
|
|
@ -7,3 +7,4 @@ org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
|||
#org.eclipse.jetty.spdy.server.http.ReferrerPushStrategy.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.spdy.server.proxy.LEVEL=DEBUG
|
||||
#org.mortbay.LEVEL=DEBUG
|
||||
|
||||
|
|
Loading…
Reference in New Issue