450855 - GZipFilter declaration order can result in MIGHT_COMPRESS

+ Adding testcase GzipFilterLayeredTest to demonstrate this bug
+ Some cleanup of Gzip testing behavior to be easier to follow
  (less reliance on GzipTester utility class)
This commit is contained in:
Joakim Erdfelt 2014-11-10 11:22:22 -07:00
parent e4cc9ea5de
commit 9e8a776c3e
6 changed files with 560 additions and 114 deletions

View File

@ -34,11 +34,16 @@ import org.eclipse.jetty.server.LocalConnector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.util.Attributes;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
public class ServletTester extends ContainerLifeCycle
{
private static final Logger LOG = Log.getLogger(ServletTester.class);
private final Server _server=new Server();
private final LocalConnector _connector=new LocalConnector(_server);
private final ServletContextHandler _context;
@ -163,8 +168,6 @@ public class ServletTester extends ContainerLifeCycle
_context.setResourceBase(resourceBase);
}
private final ServletHandler _handler;
public ServletTester()
{
this("/",ServletContextHandler.SECURITY|ServletContextHandler.SESSIONS);
@ -178,7 +181,6 @@ public class ServletTester extends ContainerLifeCycle
public ServletTester(String contextPath,int options)
{
_context=new ServletContextHandler(_server,contextPath,options);
_handler=_context.getServletHandler();
_server.setConnectors(new Connector[]{_connector});
addBean(_server);
}
@ -190,25 +192,40 @@ public class ServletTester extends ContainerLifeCycle
public String getResponses(String request) throws Exception
{
if (LOG.isDebugEnabled())
{
LOG.debug("Request: {}",request);
}
return _connector.getResponses(request);
}
public String getResponses(String request, long idleFor,TimeUnit units) throws Exception
{
if (LOG.isDebugEnabled())
{
LOG.debug("Request: {}",request);
}
return _connector.getResponses(request, idleFor, units);
}
public ByteBuffer getResponses(ByteBuffer request) throws Exception
{
if (LOG.isDebugEnabled())
{
LOG.debug("Request (Buffer): {}",BufferUtil.toUTF8String(request));
}
return _connector.getResponses(request);
}
public ByteBuffer getResponses(ByteBuffer requestsBuffer,long idleFor,TimeUnit units) throws Exception
{
if (LOG.isDebugEnabled())
{
LOG.debug("Requests (Buffer): {}",BufferUtil.toUTF8String(requestsBuffer));
}
return _connector.getResponses(requestsBuffer, idleFor, units);
}
/* ------------------------------------------------------------ */
/** Create a port based connector.
* This methods adds a port connector to the server
* @return A URL to access the server via the connector.

View File

@ -0,0 +1,184 @@
//
// ========================================================================
// Copyright (c) 1995-2014 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 static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.io.File;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.servlet.DispatcherType;
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.AsyncManipFilter;
import org.eclipse.jetty.servlets.gzip.GzipTester;
import org.eclipse.jetty.servlets.gzip.GzipTester.ContentMetadata;
import org.eclipse.jetty.servlets.gzip.TestServletLengthStreamTypeWrite;
import org.eclipse.jetty.toolchain.test.TestTracker;
import org.eclipse.jetty.toolchain.test.TestingDir;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
/**
* Test the GzipFilter support when under several layers of Filters.
*/
@RunWith(Parameterized.class)
public class GzipFilterLayeredTest
{
@Rule
public final TestTracker tracker = new TestTracker();
private static final HttpConfiguration defaultHttp = new HttpConfiguration();
private static final int LARGE = defaultHttp.getOutputBufferSize() * 8;
private static final int SMALL = defaultHttp.getOutputBufferSize() / 4;
private static final int TINY = AsyncGzipFilter.DEFAULT_MIN_GZIP_SIZE / 2;
private static final boolean EXPECT_COMPRESSED = true;
@Parameters(name = "{0} bytes - {1} - compressed: {2}")
public static List<Object[]> data()
{
List<Object[]> ret = new ArrayList<Object[]>();
ret.add(new Object[] { 0, "empty.txt", !EXPECT_COMPRESSED });
ret.add(new Object[] { TINY, "file-tiny.txt", !EXPECT_COMPRESSED });
ret.add(new Object[] { SMALL, "file-small.txt", EXPECT_COMPRESSED });
ret.add(new Object[] { LARGE, "file-large.txt", EXPECT_COMPRESSED });
ret.add(new Object[] { LARGE, "file-large.mp3", !EXPECT_COMPRESSED });
return ret;
}
@Parameter(0)
public int fileSize;
@Parameter(1)
public String fileName;
@Parameter(2)
public boolean expectCompressed;
@Rule
public TestingDir testingdir = new TestingDir();
@Test
@Ignore
public void testGzipDosNormal() throws Exception
{
GzipTester tester = new GzipTester(testingdir, GzipFilter.GZIP);
// Add Gzip Filter first
FilterHolder gzipHolder = new FilterHolder(AsyncGzipFilter.class);
gzipHolder.setAsyncSupported(true);
tester.addFilter(gzipHolder,"*.txt",EnumSet.of(DispatcherType.REQUEST,DispatcherType.ASYNC));
tester.addFilter(gzipHolder,"*.mp3",EnumSet.of(DispatcherType.REQUEST,DispatcherType.ASYNC));
gzipHolder.setInitParameter("mimeTypes","text/plain");
// Add (DoSFilter-like) manip filter (in chain of Gzip)
FilterHolder manipHolder = new FilterHolder(AsyncManipFilter.class);
manipHolder.setAsyncSupported(true);
tester.addFilter(manipHolder,"/*",EnumSet.of(DispatcherType.REQUEST,DispatcherType.ASYNC));
// Add normal content servlet
tester.setContentServlet(TestServletLengthStreamTypeWrite.class);
try
{
File testFile = tester.prepareServerFile("GzipDosNormal-" + fileName,fileSize);
tester.start();
HttpTester.Response response = tester.issueRequest("GET","/" + testFile.getName(), 2, TimeUnit.SECONDS);
assertThat("Response status", response.getStatus(), is(HttpStatus.OK_200));
if (expectCompressed)
{
// Must be gzip compressed
assertThat("Content-Encoding",response.get("Content-Encoding"),containsString(GzipFilter.GZIP));
}
// Uncompressed content Size
ContentMetadata content = tester.getResponseMetadata(response);
assertThat("(Uncompressed) Content Length", content.size, is((long)fileSize));
}
finally
{
tester.stop();
}
}
@Test
public void testDosGzipNormal() throws Exception
{
GzipTester tester = new GzipTester(testingdir, GzipFilter.GZIP);
// Add (DoSFilter-like) manip filter
FilterHolder manipHolder = new FilterHolder(AsyncManipFilter.class);
manipHolder.setAsyncSupported(true);
tester.addFilter(manipHolder,"/*",EnumSet.of(DispatcherType.REQUEST,DispatcherType.ASYNC));
// Add Gzip Filter first (in chain of DosFilter)
FilterHolder gzipHolder = new FilterHolder(AsyncGzipFilter.class);
gzipHolder.setAsyncSupported(true);
tester.addFilter(gzipHolder,"*.txt",EnumSet.of(DispatcherType.REQUEST,DispatcherType.ASYNC));
tester.addFilter(gzipHolder,"*.mp3",EnumSet.of(DispatcherType.REQUEST,DispatcherType.ASYNC));
gzipHolder.setInitParameter("mimeTypes","text/plain");
// Add normal content servlet
tester.setContentServlet(TestServletLengthStreamTypeWrite.class);
try
{
File testFile = tester.prepareServerFile("DosGzipNormal-" + fileName,fileSize);
tester.start();
HttpTester.Response response = tester.issueRequest("GET",testFile.getName(),2,TimeUnit.SECONDS);
assertThat("Response status", response.getStatus(), is(HttpStatus.OK_200));
if (expectCompressed)
{
// Must be gzip compressed
assertThat("Content-Encoding",response.get("Content-Encoding"),containsString(GzipFilter.GZIP));
} else
{
assertThat("Content-Encoding",response.get("Content-Encoding"),not(containsString(GzipFilter.GZIP)));
}
// Uncompressed content Size
ContentMetadata content = tester.getResponseMetadata(response);
assertThat("(Uncompressed) Content Length", content.size, is((long)fileSize));
}
finally
{
tester.stop();
}
}
}

View File

@ -0,0 +1,103 @@
//
// ========================================================================
// Copyright (c) 1995-2014 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 javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/**
* Filter that merely manipulates the AsyncContext.
* <p>
* The pattern of manipulation is modeled after how DOSFilter behaves. The purpose of this filter is to test arbitrary filter chains that could see unintended
* side-effects of async context manipulation.
*/
public class AsyncManipFilter implements Filter, AsyncListener
{
private static final Logger LOG = Log.getLogger(AsyncManipFilter.class);
private static final String MANIP_KEY = AsyncManipFilter.class.getName();
@Override
public void init(FilterConfig filterConfig) throws ServletException
{
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
LOG.debug("doFilter() - {}", chain);
AsyncContext ctx = (AsyncContext)request.getAttribute(MANIP_KEY);
if (ctx == null)
{
LOG.debug("Initial pass through: {}", chain);
ctx = request.startAsync();
ctx.addListener(this);
ctx.setTimeout(1000);
LOG.debug("AsyncContext: {}", ctx);
request.setAttribute(MANIP_KEY,ctx);
return;
}
else
{
LOG.debug("Second pass through: {}", chain);
chain.doFilter(request,response);
}
}
@Override
public void destroy()
{
}
@Override
public void onComplete(AsyncEvent event) throws IOException
{
LOG.debug("onComplete() {}",event);
}
@Override
public void onTimeout(AsyncEvent event) throws IOException
{
LOG.debug("onTimeout() {}",event.getAsyncContext());
event.getAsyncContext().dispatch();
}
@Override
public void onError(AsyncEvent event) throws IOException
{
LOG.debug("onError()",event.getThrowable());
}
@Override
public void onStartAsync(AsyncEvent event) throws IOException
{
LOG.debug("onTimeout() {}",event);
}
}

View File

@ -18,13 +18,8 @@
package org.eclipse.jetty.servlets.gzip;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@ -37,6 +32,7 @@ import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.util.EnumSet;
import java.util.Enumeration;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;
@ -51,6 +47,7 @@ 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.http.HttpTester.Response;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletHolder;
@ -67,8 +64,20 @@ import org.junit.Assert;
public class GzipTester
{
private static final Logger LOG = Log.getLogger(GzipTester.class);
private Class<? extends Filter> gzipFilterClass = GzipFilter.class;
public static class ContentMetadata
{
public final long size;
public final String sha1;
public ContentMetadata(long size, String sha1checksum)
{
this.size = size;
this.sha1 = sha1checksum;
}
}
private Class<? extends Filter> gzipFilterClass = null;
private String encoding = "ISO8859_1";
private String userAgent = null;
private final ServletTester tester = new ServletTester();;
@ -80,14 +89,14 @@ public class GzipTester
{
this.testdir = testingdir;
this.compressionType = compressionType;
this.accept=accept;
this.accept = accept;
}
public GzipTester(TestingDir testingdir, String compressionType)
{
this.testdir = testingdir;
this.compressionType = compressionType;
this.accept=compressionType;
this.accept = compressionType;
}
public int getOutputBufferSize()
@ -95,11 +104,88 @@ public class GzipTester
return tester.getConnector().getConnectionFactory(HttpConnectionFactory.class).getHttpConfiguration().getOutputBufferSize();
}
public HttpTester.Response issueRequest(String method, String path) throws Exception
{
return issueRequest(method, path, 2, TimeUnit.SECONDS);
}
public HttpTester.Response issueRequest(String method, String path, int idleFor, TimeUnit idleUnit) throws Exception
{
HttpTester.Request request = HttpTester.newRequest();
request.setMethod(method);
request.setVersion("HTTP/1.1");
request.setHeader("Host","tester");
request.setHeader("Accept-Encoding",accept);
request.setHeader("Connection","close");
if (this.userAgent != null)
{
request.setHeader("User-Agent",this.userAgent);
}
request.setURI("/context/" + path);
// Issue the request
return HttpTester.parseResponse(tester.getResponses(request.generate(),idleFor,idleUnit));
}
public ContentMetadata getResponseMetadata(Response response) throws Exception
{
long size = response.getContentBytes().length;
String contentEncoding = response.get("Content-Encoding");
ByteArrayInputStream bais = null;
InputStream in = null;
DigestOutputStream digester = null;
ByteArrayOutputStream uncompressedStream = null;
try
{
MessageDigest digest = MessageDigest.getInstance("SHA1");
bais = new ByteArrayInputStream(response.getContentBytes());
if (contentEncoding == null)
{
LOG.debug("No response content-encoding");
in = new PassThruInputStream(bais);
}
else if (contentEncoding.contains(GzipFilter.GZIP))
{
in = new GZIPInputStream(bais);
}
else if (contentEncoding.contains(GzipFilter.DEFLATE))
{
in = new InflaterInputStream(bais,new Inflater(true));
}
else
{
assertThat("Unexpected response content-encoding", contentEncoding, isEmptyOrNullString());
}
uncompressedStream = new ByteArrayOutputStream((int)size);
digester = new DigestOutputStream(uncompressedStream,digest);
IO.copy(in,digester);
byte output[] = uncompressedStream.toByteArray();
String actualSha1Sum = Hex.asHex(digest.digest());
return new ContentMetadata(output.length,actualSha1Sum);
}
finally
{
IO.close(digester);
IO.close(in);
IO.close(bais);
IO.close(uncompressedStream);
}
}
public HttpTester.Response assertIsResponseGzipCompressed(String method, String filename) throws Exception
{
return assertIsResponseGzipCompressed(method,filename,filename,-1);
}
public HttpTester.Response assertIsResponseGzipCompressed(String method, String filename, long ifmodifiedsince) throws Exception
{
return assertIsResponseGzipCompressed(method,filename,filename,ifmodifiedsince);
@ -109,7 +195,6 @@ public class GzipTester
{
return assertIsResponseGzipCompressed(method,requestedFilename,serverFilename,-1);
}
public HttpTester.Response assertNonStaticContentIsResponseGzipCompressed(String method, String path, String expected) throws Exception
{
@ -120,25 +205,25 @@ public class GzipTester
request.setVersion("HTTP/1.0");
request.setHeader("Host","tester");
request.setHeader("Accept-Encoding",accept);
if (this.userAgent != null)
request.setHeader("User-Agent", this.userAgent);
request.setHeader("User-Agent",this.userAgent);
request.setURI("/context/" + path);
// Issue the request
response = HttpTester.parseResponse(tester.getResponses(request.generate()));
int qindex = compressionType.indexOf(";");
if (qindex < 0)
Assert.assertThat("Response.header[Content-Encoding]",response.get("Content-Encoding"),containsString(compressionType));
else
Assert.assertThat("Response.header[Content-Encoding]", response.get("Content-Encoding"),containsString(compressionType.substring(0,qindex)));
Assert.assertThat("Response.header[Content-Encoding]",response.get("Content-Encoding"),containsString(compressionType.substring(0,qindex)));
ByteArrayInputStream bais = null;
InputStream in = null;
ByteArrayOutputStream out = null;
String actual = null;
ByteArrayInputStream bais = null;
InputStream in = null;
ByteArrayOutputStream out = null;
String actual = null;
try
{
bais = new ByteArrayInputStream(response.getContentBytes());
@ -148,7 +233,7 @@ public class GzipTester
}
else if (compressionType.startsWith(GzipFilter.DEFLATE))
{
in = new InflaterInputStream(bais, new Inflater(true));
in = new InflaterInputStream(bais,new Inflater(true));
}
out = new ByteArrayOutputStream();
IO.copy(in,out);
@ -162,12 +247,12 @@ public class GzipTester
IO.close(in);
IO.close(bais);
}
return response;
}
public HttpTester.Response assertIsResponseGzipCompressed(String method, String requestedFilename, String serverFilename, long ifmodifiedsince) throws Exception
public HttpTester.Response assertIsResponseGzipCompressed(String method, String requestedFilename, String serverFilename, long ifmodifiedsince)
throws Exception
{
HttpTester.Request request = HttpTester.newRequest();
HttpTester.Response response;
@ -176,39 +261,36 @@ public class GzipTester
request.setVersion("HTTP/1.0");
request.setHeader("Host","tester");
request.setHeader("Accept-Encoding",compressionType);
if (ifmodifiedsince>0)
if (ifmodifiedsince > 0)
request.setHeader(HttpHeader.IF_MODIFIED_SINCE.asString(),DateGenerator.formatDate(ifmodifiedsince));
if (this.userAgent != null)
request.setHeader("User-Agent", this.userAgent);
request.setHeader("User-Agent",this.userAgent);
request.setURI("/context/" + requestedFilename);
// Issue the request
response = HttpTester.parseResponse(tester.getResponses(request.generate()));
// Assert the response headers
// Assert.assertThat("Response.status",response.getStatus(),is(HttpServletResponse.SC_OK));
// Response headers should have either a Transfer-Encoding indicating chunked OR a Content-Length
/* TODO need to check for the 3rd option of EOF content. To do this properly you might need to look at both HTTP/1.1 and HTTP/1.0 requests
String contentLength = response.get("Content-Length");
String transferEncoding = response.get("Transfer-Encoding");
boolean chunked = (transferEncoding != null) && (transferEncoding.indexOf("chunk") >= 0);
if(!chunked) {
Assert.assertThat("Response.header[Content-Length]",contentLength,notNullValue());
} else {
Assert.assertThat("Response.header[Transfer-Encoding]",transferEncoding,notNullValue());
}
*/
/*
* TODO need to check for the 3rd option of EOF content. To do this properly you might need to look at both HTTP/1.1 and HTTP/1.0 requests String
* contentLength = response.get("Content-Length"); String transferEncoding = response.get("Transfer-Encoding");
*
* boolean chunked = (transferEncoding != null) && (transferEncoding.indexOf("chunk") >= 0); if(!chunked) {
* Assert.assertThat("Response.header[Content-Length]",contentLength,notNullValue()); } else {
* Assert.assertThat("Response.header[Transfer-Encoding]",transferEncoding,notNullValue()); }
*/
int qindex = compressionType.indexOf(";");
if (qindex < 0)
Assert.assertThat("Response.header[Content-Encoding]",response.get("Content-Encoding"),containsString(compressionType));
else
Assert.assertThat("Response.header[Content-Encoding]", response.get("Content-Encoding"),containsString(compressionType.substring(0,qindex)));
Assert.assertThat("Response.header[Content-Encoding]",response.get("Content-Encoding"),containsString(compressionType.substring(0,qindex)));
Assert.assertThat(response.get("ETag"),Matchers.startsWith("W/"));
// Assert that the decompressed contents are what we expect.
File serverFile = testdir.getFile(serverFilename);
String expected = IO.readToString(serverFile);
@ -226,7 +308,7 @@ public class GzipTester
}
else if (compressionType.startsWith(GzipFilter.DEFLATE))
{
in = new InflaterInputStream(bais, new Inflater(true));
in = new InflaterInputStream(bais,new Inflater(true));
}
out = new ByteArrayOutputStream();
IO.copy(in,out);
@ -240,11 +322,10 @@ public class GzipTester
IO.close(in);
IO.close(bais);
}
return response;
}
public HttpTester.Response assertIsResponseNotModified(String method, String requestedFilename, long ifmodifiedsince) throws Exception
{
HttpTester.Request request = HttpTester.newRequest();
@ -254,10 +335,10 @@ public class GzipTester
request.setVersion("HTTP/1.0");
request.setHeader("Host","tester");
request.setHeader("Accept-Encoding",compressionType);
if (ifmodifiedsince>0)
if (ifmodifiedsince > 0)
request.setHeader(HttpHeader.IF_MODIFIED_SINCE.asString(),DateGenerator.formatDate(ifmodifiedsince));
if (this.userAgent != null)
request.setHeader("User-Agent", this.userAgent);
request.setHeader("User-Agent",this.userAgent);
request.setURI("/context/" + requestedFilename);
// Issue the request
@ -265,47 +346,45 @@ public class GzipTester
Assert.assertThat(response.getStatus(),Matchers.equalTo(304));
Assert.assertThat(response.get("ETag"),Matchers.startsWith("W/"));
return response;
}
/**
* 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.
* 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.
* the sha1sum file that contains the SHA1SUM checksum that will be used to verify that the response contents are what is intended.
* @param expectedContentType
*/
public void assertIsResponseNotGzipFiltered(String requestedFilename, String testResourceSha1Sum, String expectedContentType) throws Exception
{
assertIsResponseNotGzipFiltered(requestedFilename, testResourceSha1Sum, expectedContentType,null);
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.
* 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.
* 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
* @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
public void assertIsResponseNotGzipFiltered(String requestedFilename, String testResourceSha1Sum, String expectedContentType, String expectedContentEncoding)
throws Exception
{
HttpTester.Request request = HttpTester.newRequest();
HttpTester.Response response;
@ -315,7 +394,7 @@ public class GzipTester
request.setHeader("Host","tester");
request.setHeader("Accept-Encoding",compressionType);
if (this.userAgent != null)
request.setHeader("User-Agent", this.userAgent);
request.setHeader("User-Agent",this.userAgent);
request.setURI("/context/" + requestedFilename);
// Issue the request
@ -328,14 +407,14 @@ public class GzipTester
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-Encoding] (should not be recompressed by GzipFilter)",response.get("Content-Encoding"),
expectedContentEncoding == null? nullValue() : notNullValue());
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]",response.get("Content-Type"),is(expectedContentType));
Assert.assertThat(response.get("ETAG"),Matchers.startsWith("W/"));
ByteArrayInputStream bais = null;
DigestOutputStream digester = null;
try
@ -358,7 +437,7 @@ public class GzipTester
private void dumpHeaders(String prefix, HttpTester.Message message)
{
LOG.debug("dumpHeaders: {}", prefix);
LOG.debug("dumpHeaders: {}",prefix);
Enumeration<String> names = message.getFieldNames();
while (names.hasMoreElements())
{
@ -379,21 +458,20 @@ public class GzipTester
}
/**
* Asserts that the requested filename results in a properly structured GzipFilter response, where the content is
* not compressed, and the content-length is returned appropriately.
* Asserts that the requested filename results in a properly structured GzipFilter response, where the content is not compressed, and the content-length is
* returned appropriately.
*
* @param filename
* the filename used for the request, and also used to compare the response to the server file, assumes
* that the file is suitable for {@link Assert#assertEquals(Object, Object)} use. (in other words, the
* contents of the file are text)
* the filename used for the request, and also used to compare the response to the server file, assumes that the file is suitable for
* {@link Assert#assertEquals(Object, Object)} use. (in other words, the contents of the file are text)
* @param expectedFilesize
* the expected filesize to be specified on the Content-Length portion of the response headers. (note:
* passing -1 will disable the Content-Length assertion)
* the expected filesize to be specified on the Content-Length portion of the response headers. (note: passing -1 will disable the Content-Length
* assertion)
* @throws Exception
*/
public HttpTester.Response assertIsResponseNotGzipCompressed(String method, String filename, int expectedFilesize, int status) throws Exception
{
String uri = "/context/"+filename;
String uri = "/context/" + filename;
HttpTester.Response response = executeRequest(method,uri);
assertResponseHeaders(expectedFilesize,status,response);
// Assert that the contents are what we expect.
@ -405,23 +483,23 @@ public class GzipTester
String actual = readResponse(response);
Assert.assertEquals("Expected response equals actual response",expectedResponse,actual);
}
return response;
}
/**
* Asserts that the request results in a properly structured GzipFilter response, where the content is
* not compressed, and the content-length is returned appropriately.
* Asserts that the request results in a properly structured GzipFilter response, where the content is not compressed, and the content-length is returned
* appropriately.
*
* @param expectedResponse
* the expected response body string
* @param expectedFilesize
* the expected filesize to be specified on the Content-Length portion of the response headers. (note:
* passing -1 will disable the Content-Length assertion)
* the expected filesize to be specified on the Content-Length portion of the response headers. (note: passing -1 will disable the Content-Length
* assertion)
* @throws Exception
*/
public void assertIsResponseNotGzipCompressedAndEqualToExpectedString(String method,String expectedResponse, int expectedFilesize, int status) throws Exception
public void assertIsResponseNotGzipCompressedAndEqualToExpectedString(String method, String expectedResponse, int expectedFilesize, int status)
throws Exception
{
String uri = "/context/";
HttpTester.Response response = executeRequest(method,uri);
@ -432,15 +510,15 @@ public class GzipTester
}
/**
* Asserts that the request results in a properly structured GzipFilter response, where the content is
* not compressed, and the content-length is returned appropriately.
* Asserts that the request results in a properly structured GzipFilter response, where the content is not compressed, and the content-length is returned
* appropriately.
*
* @param expectedFilesize
* the expected filesize to be specified on the Content-Length portion of the response headers. (note:
* passing -1 will disable the Content-Length assertion)
* the expected filesize to be specified on the Content-Length portion of the response headers. (note: passing -1 will disable the Content-Length
* assertion)
* @throws Exception
*/
public void assertIsResponseNotGzipCompressed(String method,int expectedFilesize, int status) throws Exception
public void assertIsResponseNotGzipCompressed(String method, int expectedFilesize, int status) throws Exception
{
String uri = "/context/";
HttpTester.Response response = executeRequest(method,uri);
@ -454,16 +532,16 @@ public class GzipTester
if (expectedFilesize != (-1))
{
Assert.assertEquals(expectedFilesize,response.getContentBytes().length);
String cl=response.get("Content-Length");
if (cl!=null)
String cl = response.get("Content-Length");
if (cl != null)
{
int serverLength = Integer.parseInt(response.get("Content-Length"));
Assert.assertEquals(serverLength,expectedFilesize);
}
if (status>=200 && status<300)
Assert.assertThat(response.get("ETAG"),Matchers.startsWith("W/"));
if (status >= 200 && status < 300)
Assert.assertThat(response.get("ETAG"),Matchers.startsWith("W/"));
}
Assert.assertThat("Response.header[Content-Encoding]",response.get("Content-Encoding"),not(containsString(compressionType)));
}
@ -478,7 +556,7 @@ public class GzipTester
request.setHeader("Host","tester");
request.setHeader("Accept-Encoding",compressionType);
if (this.userAgent != null)
request.setHeader("User-Agent", this.userAgent);
request.setHeader("User-Agent",this.userAgent);
request.setURI(uri);
response = HttpTester.parseResponse(tester.getResponses(request.generate()));
@ -492,11 +570,11 @@ public class GzipTester
ByteArrayOutputStream out = null;
try
{
byte[] content=response.getContentBytes();
if (content!=null)
actual=new String(response.getContentBytes(),encoding);
byte[] content = response.getContentBytes();
if (content != null)
actual = new String(response.getContentBytes(),encoding);
else
actual="";
actual = "";
}
finally
{
@ -506,7 +584,6 @@ public class GzipTester
return actual;
}
/**
* Generate string content of arbitrary length.
*
@ -579,7 +656,7 @@ public class GzipTester
finally
{
IO.close(in);
IO.close(fos);
IO.close(fos);
}
}
@ -611,9 +688,17 @@ public class GzipTester
ServletHolder servletHolder = tester.addServlet(servletClass,"/");
servletHolder.setInitParameter("baseDir",testdir.getDir().getAbsolutePath());
servletHolder.setInitParameter("etags","true");
FilterHolder holder = tester.addFilter(gzipFilterClass,"/*",EnumSet.of(DispatcherType.REQUEST));
holder.setInitParameter("vary","Accept-Encoding");
return holder;
if (gzipFilterClass != null)
{
FilterHolder holder = tester.addFilter(gzipFilterClass,"/*",EnumSet.of(DispatcherType.REQUEST));
holder.setInitParameter("vary","Accept-Encoding");
return holder;
}
else
{
return null;
}
}
public Class<? extends Filter> getGzipFilterClass()
@ -635,22 +720,41 @@ public class GzipTester
{
this.userAgent = ua;
}
public void addMimeType (String extension, String mimetype)
public void addMimeType(String extension, String mimetype)
{
this.tester.getContext().getMimeTypes().addMimeMapping(extension, mimetype);
this.tester.getContext().getMimeTypes().addMimeMapping(extension,mimetype);
}
/**
* Add an arbitrary filter to the test case.
*
* @param holder
* the filter to add
* @param pathSpec
* the path spec for this filter
* @param dispatches
* the set of {@link DispatcherType} to associate with this filter
*/
public void addFilter(FilterHolder holder, String pathSpec, EnumSet<DispatcherType> dispatches) throws IOException
{
tester.addFilter(holder,pathSpec,dispatches);
}
public void start() throws Exception
{
Assert.assertThat("No servlet defined yet. Did you use #setContentServlet()?",tester,notNullValue());
tester.dump();
if (LOG.isDebugEnabled())
{
tester.dumpStdErr();
}
tester.start();
}
public void stop()
{
// NOTE: Do not cleanup the testdir. Failures can't be diagnosed if you do that.
// NOTE: Do not cleanup the testdir. Failures can't be diagnosed if you do that.
// IO.delete(testdir.getDir()):
try
{
@ -664,4 +768,5 @@ public class GzipTester
e.printStackTrace(System.err);
}
}
}

View File

@ -0,0 +1,36 @@
//
// ========================================================================
// Copyright (c) 1995-2014 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.FilterInputStream;
import java.io.InputStream;
/**
* A simple pass-through input stream.
* <p>
* Used in some test cases where a proper resource open/close is needed for
* some potentially optional layers of the input stream.
*/
public class PassThruInputStream extends FilterInputStream
{
public PassThruInputStream(InputStream in)
{
super(in);
}
}

View File

@ -1,6 +1,7 @@
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.servlet.ServletTester.LEVEL=DEBUG
#org.eclipse.jetty.servlets.GzipFilter.LEVEL=DEBUG
#org.eclipse.jetty.servlets.QoSFilter.LEVEL=DEBUG
#org.eclipse.jetty.servlets.DoSFilter.LEVEL=DEBUG