436894 GzipFilter code cleanup

This commit is contained in:
Jan Bartel 2014-06-09 18:35:49 +02:00
parent e5910e576e
commit 144a460407
11 changed files with 2248 additions and 13 deletions

View File

@ -141,6 +141,7 @@ src=application/x-wais-source
sv4cpio=application/x-sv4cpio sv4cpio=application/x-sv4cpio
sv4crc=application/x-sv4crc sv4crc=application/x-sv4crc
svg=image/svg+xml svg=image/svg+xml
svgz=image/svg+xml
swf=application/x-shockwave-flash swf=application/x-shockwave-flash
t=application/x-troff t=application/x-troff
tar=application/x-tar tar=application/x-tar

View File

@ -25,8 +25,12 @@ import java.io.OutputStream;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import javax.servlet.AsyncContext; import javax.servlet.AsyncContext;
import javax.servlet.RequestDispatcher; import javax.servlet.RequestDispatcher;
@ -125,6 +129,9 @@ import org.eclipse.jetty.util.resource.ResourceFactory;
* *
* cacheControl If set, all static content will have this value set as the cache-control * cacheControl If set, all static content will have this value set as the cache-control
* header. * header.
*
* otherGzipFileExtensions
* Other file extensions that signify that a file is gzip compressed. Eg ".svgz"
* *
* *
* </PRE> * </PRE>
@ -164,6 +171,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
private String _relativeResourceBase; private String _relativeResourceBase;
private ServletHandler _servletHandler; private ServletHandler _servletHandler;
private ServletHolder _defaultHolder; private ServletHolder _defaultHolder;
private List<String> _gzipEquivalentFileExtensions;
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
@Override @Override
@ -273,6 +281,24 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
LOG.warn(Log.EXCEPTION,e); LOG.warn(Log.EXCEPTION,e);
throw new UnavailableException(e.toString()); throw new UnavailableException(e.toString());
} }
_gzipEquivalentFileExtensions = new ArrayList<String>();
String otherGzipExtensions = getInitParameter("otherGzipFileExtensions");
if (otherGzipExtensions != null)
{
//comma separated list
StringTokenizer tok = new StringTokenizer(otherGzipExtensions,",",false);
while (tok.hasMoreTokens())
{
String s = tok.nextToken().trim();
_gzipEquivalentFileExtensions.add((s.charAt(0)=='.'?s:"."+s));
}
}
else
{
//.svgz files are gzipped svg files and must be served with Content-Encoding:gzip
_gzipEquivalentFileExtensions.add(".svgz");
}
_servletHandler= _contextHandler.getChildHandlerByClass(ServletHandler.class); _servletHandler= _contextHandler.getChildHandlerByClass(ServletHandler.class);
for (ServletHolder h :_servletHandler.getServlets()) for (ServletHolder h :_servletHandler.getServlets())
@ -496,7 +522,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
if (included.booleanValue() || passConditionalHeaders(request,response, resource,content)) if (included.booleanValue() || passConditionalHeaders(request,response, resource,content))
{ {
if (gzip) if (gzip || isGzippedContent(pathInContext))
{ {
response.setHeader(HttpHeader.CONTENT_ENCODING.asString(),"gzip"); response.setHeader(HttpHeader.CONTENT_ENCODING.asString(),"gzip");
String mt=_servletContext.getMimeType(pathInContext); String mt=_servletContext.getMimeType(pathInContext);
@ -585,6 +611,20 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory
} }
/**
* @param resource
* @return
*/
protected boolean isGzippedContent(String path)
{
if (path == null) return false;
for (String suffix:_gzipEquivalentFileExtensions)
if (path.endsWith(suffix))
return true;
return false;
}
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
private boolean hasDefinedRange(Enumeration<String> reqRanges) private boolean hasDefinedRange(Enumeration<String> reqRanges)
{ {

View File

@ -209,14 +209,16 @@ public class AsyncGzipFilter extends UserAgentFilter implements GzipFactory
{ {
for (String type:MimeTypes.getKnownMimeTypes()) for (String type:MimeTypes.getKnownMimeTypes())
{ {
if (type.equals("image/svg+xml")) //always compressable (unless .svgz file)
continue;
if (type.startsWith("image/")|| if (type.startsWith("image/")||
type.startsWith("audio/")|| type.startsWith("audio/")||
type.startsWith("video/")) type.startsWith("video/"))
_mimeTypes.add(type); _mimeTypes.add(type);
_mimeTypes.add("application/compress");
_mimeTypes.add("application/zip");
_mimeTypes.add("application/gzip");
} }
_mimeTypes.add("application/compress");
_mimeTypes.add("application/zip");
_mimeTypes.add("application/gzip");
} }
else else
{ {
@ -317,11 +319,11 @@ public class AsyncGzipFilter extends UserAgentFilter implements GzipFactory
} }
// Exclude non compressible mime-types known from URI extension. - no Vary because no matter what client, this URI is always excluded // 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) if (_mimeTypes.size()>0 && _excludeMimeTypes)
{ {
String mimeType = _context.getMimeType(request.getRequestURI()); String mimeType = _context.getMimeType(request.getRequestURI());
if (mimeType!=null && _mimeTypes.contains(mimeType)==_excludeMimeTypes) if (mimeType!=null && _mimeTypes.contains(mimeType))
{ {
LOG.debug("{} excluded by path suffix {}",this,request); LOG.debug("{} excluded by path suffix {}",this,request);
// handle normally without setting vary header // handle normally without setting vary header
@ -330,6 +332,13 @@ public class AsyncGzipFilter extends UserAgentFilter implements GzipFactory
} }
} }
//If the Content-Encoding is already set, then we won't compress
if (response.getHeader("Content-Encoding") != null)
{
super.doFilter(request,response,chain);
return;
}
if (_checkGzExists && request.getServletContext()!=null) if (_checkGzExists && request.getServletContext()!=null)
{ {
String path=request.getServletContext().getRealPath(URIUtil.addPaths(request.getServletPath(),request.getPathInfo())); String path=request.getServletContext().getRealPath(URIUtil.addPaths(request.getServletPath(),request.getPathInfo()));

View File

@ -208,14 +208,16 @@ public class GzipFilter extends UserAgentFilter
{ {
for (String type:MimeTypes.getKnownMimeTypes()) for (String type:MimeTypes.getKnownMimeTypes())
{ {
if (type.equals("image/svg+xml")) //always compressable (unless .svgz file)
continue;
if (type.startsWith("image/")|| if (type.startsWith("image/")||
type.startsWith("audio/")|| type.startsWith("audio/")||
type.startsWith("video/")) type.startsWith("video/"))
_mimeTypes.add(type); _mimeTypes.add(type);
_mimeTypes.add("application/compress");
_mimeTypes.add("application/zip");
_mimeTypes.add("application/gzip");
} }
_mimeTypes.add("application/compress");
_mimeTypes.add("application/zip");
_mimeTypes.add("application/gzip");
} }
else else
{ {
@ -300,17 +302,24 @@ public class GzipFilter extends UserAgentFilter
} }
// Exclude non compressible mime-types known from URI extension. - no Vary because no matter what client, this URI is always excluded // 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) if (_mimeTypes.size()>0 && _excludeMimeTypes)
{ {
String mimeType = _context.getMimeType(request.getRequestURI()); String mimeType = _context.getMimeType(request.getRequestURI());
if (mimeType!=null && _mimeTypes.contains(mimeType)==_excludeMimeTypes) if (mimeType!=null && _mimeTypes.contains(mimeType))
{ {
// handle normally without setting vary header // handle normally without setting vary header
super.doFilter(request,response,chain); super.doFilter(request,response,chain);
return; return;
} }
} }
//If the Content-Encoding is already set, then we won't compress
if (response.getHeader("Content-Encoding") != null)
{
super.doFilter(request,response,chain);
return;
}
if (_checkGzExists && request.getServletContext()!=null) if (_checkGzExists && request.getServletContext()!=null)
{ {

View File

@ -151,6 +151,16 @@ public class GzipHttpOutput extends HttpOutput
} }
} }
// Has the Content-Encoding header already been site?
String ce=getHttpChannel().getResponse().getHeader("Content-Encoding");
if (ce != null)
{
LOG.debug("{} exclude by content-encoding {}",this,ce);
noCompression();
super.write(content,complete,callback);
return;
}
// Are we the thread that commits? // Are we the thread that commits?
if (_state.compareAndSet(GZState.MIGHT_COMPRESS,GZState.COMMITTING)) if (_state.compareAndSet(GZState.MIGHT_COMPRESS,GZState.COMMITTING))
{ {
@ -188,6 +198,7 @@ public class GzipHttpOutput extends HttpOutput
gzip(content,complete,callback); gzip(content,complete,callback);
} }
// TODO else ?
} }
public void noCompression() public void noCompression()

View File

@ -273,6 +273,24 @@ public class GzipFilterDefaultTest
} }
} }
@Test
public void testGzippedIfSVG() throws Exception
{
GzipTester tester = new GzipTester(testingdir, compressionType);
tester.setGzipFilterClass(testFilter);
tester.copyTestServerFile("test.svg");
FilterHolder holder = tester.setContentServlet(org.eclipse.jetty.servlet.DefaultServlet.class);
try
{
tester.start();
HttpTester.Response http = tester.assertIsResponseGzipCompressed("GET","test.svg",System.currentTimeMillis()-4000);
Assert.assertEquals("Accept-Encoding",http.get("Vary"));
}
finally
{
tester.stop();
}
}
@Test @Test
public void testNotGzipedIfNotModified() throws Exception public void testNotGzipedIfNotModified() throws Exception
@ -553,4 +571,24 @@ public class GzipFilterDefaultTest
tester.stop(); tester.stop();
} }
} }
@Test
public void testIsNotGzipCompressedSVGZ() throws Exception
{
GzipTester tester = new GzipTester(testingdir,compressionType);
tester.setGzipFilterClass(testFilter);
FilterHolder holder = tester.setContentServlet(DefaultServlet.class);
tester.copyTestServerFile("test.svgz");
try
{
tester.start();
tester.assertIsResponseNotGzipFiltered("test.svgz", "test.svgz.sha1", "image/svg+xml", "gzip");
}
finally
{
tester.stop();
}
}
} }

View File

@ -282,6 +282,27 @@ public class GzipTester
* @param expectedContentType * @param expectedContentType
*/ */
public void assertIsResponseNotGzipFiltered(String requestedFilename, String testResourceSha1Sum, String expectedContentType) throws Exception public void assertIsResponseNotGzipFiltered(String requestedFilename, String testResourceSha1Sum, String expectedContentType) throws Exception
{
assertIsResponseNotGzipFiltered(requestedFilename, testResourceSha1Sum, expectedContentType,null);
}
/**
* Makes sure that the response contains an unfiltered file contents.
* <p>
* This is used to test exclusions and passthroughs in the GzipFilter.
* <p>
* An example is to test that it is possible to configure GzipFilter to not recompress content that shouldn't be
* compressed by the GzipFilter.
*
* @param requestedFilename
* the filename used to on the GET request,.
* @param testResourceSha1Sum
* the sha1sum file that contains the SHA1SUM checksum that will be used to verify that the response
* contents are what is intended.
* @param expectedContentType
* @param expectedContentEncoding can be non-null in some circumstances, eg when dealing with pre-gzipped .svgz files
*/
public void assertIsResponseNotGzipFiltered(String requestedFilename, String testResourceSha1Sum, String expectedContentType, String expectedContentEncoding) throws Exception
{ {
//System.err.printf("[GzipTester] requesting /context/%s%n",requestedFilename); //System.err.printf("[GzipTester] requesting /context/%s%n",requestedFilename);
HttpTester.Request request = HttpTester.newRequest(); HttpTester.Request request = HttpTester.newRequest();
@ -304,7 +325,10 @@ public class GzipTester
String prefix = requestedFilename + " / Response"; String prefix = requestedFilename + " / Response";
Assert.assertThat(prefix + ".status",response.getStatus(),is(HttpServletResponse.SC_OK)); Assert.assertThat(prefix + ".status",response.getStatus(),is(HttpServletResponse.SC_OK));
Assert.assertThat(prefix + ".header[Content-Length]",response.get("Content-Length"),notNullValue()); Assert.assertThat(prefix + ".header[Content-Length]",response.get("Content-Length"),notNullValue());
Assert.assertThat(prefix + ".header[Content-Encoding] (should not be recompressed by GzipFilter)",response.get("Content-Encoding"),nullValue()); Assert.assertThat(prefix + ".header[Content-Encoding] (should not be recompressed by GzipFilter)",response.get("Content-Encoding"),
expectedContentEncoding == null? nullValue() : notNullValue());
if (expectedContentEncoding != null)
Assert.assertThat(prefix + ".header[Content-Encoding]",response.get("Content-Encoding"),is(expectedContentEncoding));
Assert.assertThat(prefix + ".header[Content-Type] (should have a Content-Type associated with it)",response.get("Content-Type"),notNullValue()); Assert.assertThat(prefix + ".header[Content-Type] (should have a Content-Type associated with it)",response.get("Content-Type"),notNullValue());
Assert.assertThat(prefix + ".header[Content-Type]",response.get("Content-Type"),is(expectedContentType)); Assert.assertThat(prefix + ".header[Content-Type]",response.get("Content-Type"),is(expectedContentType));
@ -339,7 +363,7 @@ public class GzipTester
{ {
String name = names.nextElement(); String name = names.nextElement();
String value = message.get(name); String value = message.get(name);
//System.out.printf(" [%s] = %s%n",name,value); //System.out.printf(" [%s] = %s%n",name,value);
} }
} }

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 70 KiB

View File

@ -0,0 +1 @@
1ccb7a0b85585d0e9bdc3863ad093d4e53a9ea68 test.svg

Binary file not shown.

View File

@ -0,0 +1 @@
62df7c3ac6ee6e4462b6abf9ef15b4e916ecf68f test.svgz