Skip to content

Commit

Permalink
Issue #6544 - Fixing broken jetty.gzip.excludedMimeTypeList propert…
Browse files Browse the repository at this point in the history
…y support

+ Adding GzipHandler tests
+ Adding Gzip module tests
+ Updating jetty-gzip.xml for
  includedMimeTypesList and
  excludedMimeTypesList behavior
+ Adding GzipHandler support for
  setIncludedMimeTypesList(String) and
  setExcludedMimeTypesList(String

Signed-off-by: Joakim Erdfelt <joakim.erdfelt@gmail.com>
  • Loading branch information
joakime committed Jul 27, 2021
1 parent 330fc0b commit 1bdb69d
Show file tree
Hide file tree
Showing 7 changed files with 265 additions and 16 deletions.
5 changes: 5 additions & 0 deletions demos/demo-simple-webapp/src/main/webapp/WEB-INF/web.xml
Expand Up @@ -6,4 +6,9 @@

<display-name>Simple Web Application</display-name>

<mime-mapping>
<extension>webp</extension>
<mime-type>image/webp</mime-type>
</mime-mapping>

</web-app>
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
4 changes: 2 additions & 2 deletions jetty-server/src/main/config/etc/jetty-gzip.xml
Expand Up @@ -18,8 +18,8 @@
<Set name="dispatcherTypes" property="jetty.gzip.dispatcherTypes"/>
<Set name="includedMethodList" property="jetty.gzip.includedMethodList"/>
<Set name="excludedMethodList" property="jetty.gzip.excludedMethodList"/>
<Set name="includedMimeTypes" property="jetty.gzip.includedMimeTypeList"/>
<Set name="excludedMimeTypes" property="jetty.gzip.excludedMimeTypeList"/>
<Set name="includedMimeTypesList" property="jetty.gzip.includedMimeTypeList"/>
<Set name="excludedMimeTypesList" property="jetty.gzip.excludedMimeTypeList"/>
<Set name="includedPaths" property="jetty.gzip.includedPathList"/>
<Set name="excludedPaths" property="jetty.gzip.excludedPathList"/>
<Set name="inflaterPool">
Expand Down
Expand Up @@ -768,6 +768,17 @@ public void setExcludedMimeTypes(String... types)
_mimeTypes.exclude(types);
}

/**
* Set the excluded filter list of MIME types (replacing any previously set)
*
* @param csvTypes The list of mime types to exclude (without charset or other parameters), CSV format
* @see #setIncludedMimeTypesList(String)
*/
public void setExcludedMimeTypesList(String csvTypes)
{
setExcludedMimeTypes(StringUtil.csvSplit(csvTypes));
}

/**
* Set the excluded filter list of Path specs (replacing any previously set)
*
Expand Down Expand Up @@ -819,6 +830,17 @@ public void setIncludedMimeTypes(String... types)
_mimeTypes.include(types);
}

/**
* Set the included filter list of MIME types (replacing any previously set)
*
* @param csvTypes The list of mime types to include (without charset or other parameters), CSV format
* @see #setExcludedMimeTypesList(String)
*/
public void setIncludedMimeTypesList(String csvTypes)
{
setIncludedMimeTypes(StringUtil.csvSplit(csvTypes));
}

/**
* Set the included filter list of Path specs (replacing any previously set)
*
Expand Down
Expand Up @@ -61,7 +61,6 @@
import static org.hamcrest.Matchers.nullValue;
import static org.junit.jupiter.api.Assertions.assertEquals;

@SuppressWarnings("serial")
public class GzipHandlerTest
{
private static final String __content =
Expand All @@ -88,6 +87,8 @@ public class GzipHandlerTest

private Server _server;
private LocalConnector _connector;
private GzipHandler gzipHandler;
private ServletContextHandler context;

@BeforeEach
public void init() throws Exception
Expand All @@ -96,25 +97,25 @@ public void init() throws Exception
_connector = new LocalConnector(_server);
_server.addConnector(_connector);

GzipHandler gzipHandler = new GzipHandler();
gzipHandler = new GzipHandler();
gzipHandler.setMinGzipSize(16);
gzipHandler.setInflateBufferSize(4096);

ServletContextHandler context = new ServletContextHandler(gzipHandler, "/ctx");
ServletHandler servlets = context.getServletHandler();
context = new ServletContextHandler(gzipHandler, "/ctx");

_server.setHandler(gzipHandler);
gzipHandler.setHandler(context);
servlets.addServletWithMapping(MicroServlet.class, "/micro");
servlets.addServletWithMapping(MicroChunkedServlet.class, "/microchunked");
servlets.addServletWithMapping(TestServlet.class, "/content");
servlets.addServletWithMapping(ForwardServlet.class, "/forward");
servlets.addServletWithMapping(IncludeServlet.class, "/include");
servlets.addServletWithMapping(EchoServlet.class, "/echo/*");
servlets.addServletWithMapping(DumpServlet.class, "/dump/*");
servlets.addServletWithMapping(AsyncServlet.class, "/async/*");
servlets.addServletWithMapping(BufferServlet.class, "/buffer/*");
servlets.addFilterWithMapping(CheckFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
context.addServlet(MicroServlet.class, "/micro");
context.addServlet(MicroChunkedServlet.class, "/microchunked");
context.addServlet(TestServlet.class, "/content");
context.addServlet(MimeTypeContentServlet.class, "/mimetypes/*");
context.addServlet(ForwardServlet.class, "/forward");
context.addServlet(IncludeServlet.class, "/include");
context.addServlet(EchoServlet.class, "/echo/*");
context.addServlet(DumpServlet.class, "/dump/*");
context.addServlet(AsyncServlet.class, "/async/*");
context.addServlet(BufferServlet.class, "/buffer/*");
context.addFilter(CheckFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));

_server.start();
}
Expand Down Expand Up @@ -147,6 +148,34 @@ protected void doGet(HttpServletRequest req, HttpServletResponse response) throw
}
}

public static class MimeTypeContentServlet extends HttpServlet
{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
String pathInfo = req.getPathInfo();
resp.setContentType(getContentTypeFromRequest(pathInfo, req));
resp.getWriter().println("This is content for " + pathInfo);
}

private String getContentTypeFromRequest(String filename, HttpServletRequest req)
{
String defaultContentType = "application/octet-stream";
if (req.getParameter("type") != null)
defaultContentType = req.getParameter("type");

ServletContextHandler servletContextHandler = ServletContextHandler.getServletContextHandler(getServletContext());
if (servletContextHandler == null)
return defaultContentType;
String contentType = servletContextHandler.getMimeTypes().getMimeByExtension(filename);
if (contentType != null)
{
return contentType;
}
return defaultContentType;
}
}

public static class TestServlet extends HttpServlet
{
@Override
Expand Down Expand Up @@ -797,6 +826,52 @@ public void testGzipBomb() throws Exception
assertThat(response.getContentBytes().length, is(512 * 1024));
}

@Test
public void testGzipExcludeNewMimeType() throws Exception
{
// setting all excluded mime-types to a mimetype new mime-type
// Note: this mime-type does not exist in MimeTypes object.
gzipHandler.setExcludedMimeTypes("image/webfoo");

// generated and parsed test
HttpTester.Request request = HttpTester.newRequest();
HttpTester.Response response;

// Request something that is not present on MimeTypes and is also
// excluded by GzipHandler configuration
request.setMethod("GET");
request.setURI("/ctx/mimetypes/foo.webfoo?type=image/webfoo");
request.setVersion("HTTP/1.1");
request.setHeader("Host", "tester");
request.setHeader("Accept", "*/*");
request.setHeader("Accept-Encoding", "gzip"); // allow compressed responses
request.setHeader("Connection", "close");

response = HttpTester.parseResponse(_connector.getResponse(request.generate()));

assertThat(response.getStatus(), is(200));
assertThat("Should not be compressed with gzip", response.get("Content-Encoding"), nullValue());
assertThat(response.get("ETag"), nullValue());
assertThat(response.get("Vary"), nullValue());

// Request something that is present on MimeTypes and is also compressible
// by the GzipHandler configuration
request.setMethod("GET");
request.setURI("/ctx/mimetypes/zed.txt");
request.setVersion("HTTP/1.1");
request.setHeader("Host", "tester");
request.setHeader("Accept", "*/*");
request.setHeader("Accept-Encoding", "gzip"); // allow compressed responses
request.setHeader("Connection", "close");

response = HttpTester.parseResponse(_connector.getResponse(request.generate()));

assertThat(response.getStatus(), is(200));
assertThat(response.get("Content-Encoding"), containsString("gzip"));
assertThat(response.get("ETag"), nullValue());
assertThat(response.get("Vary"), is("Accept-Encoding"));
}

public static class CheckFilter implements Filter
{
@Override
Expand Down
@@ -0,0 +1,147 @@
//
// ========================================================================
// Copyright (c) 1995-2021 Mort Bay Consulting Pty Ltd and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
// which is available at https://www.apache.org/licenses/LICENSE-2.0.
//
// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
// ========================================================================
//

package org.eclipse.jetty.tests.distribution;

import java.io.File;
import java.nio.file.Path;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpStatus;
import org.junit.jupiter.api.Test;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class GzipModuleTests extends AbstractJettyHomeTest
{
@Test
public void testGzipDefault() throws Exception
{
Path jettyBase = newTestJettyBaseDirectory();
String jettyVersion = System.getProperty("jettyVersion");
JettyHomeTester distribution = JettyHomeTester.Builder.newInstance()
.jettyVersion(jettyVersion)
.jettyBase(jettyBase)
.mavenLocalRepository(System.getProperty("mavenRepoPath"))
.build();

int httpPort = distribution.freePort();
int httpsPort = distribution.freePort();
assertThat("httpPort != httpsPort", httpPort, is(not(httpsPort)));

String[] argsConfig = {
"--add-modules=gzip",
"--add-modules=deploy,webapp,http"
};

try (JettyHomeTester.Run runConfig = distribution.start(argsConfig))
{
assertTrue(runConfig.awaitFor(5, TimeUnit.SECONDS));
assertEquals(0, runConfig.getExitValue());

String[] argsStart = {
"jetty.http.port=" + httpPort,
"jetty.httpConfig.port=" + httpsPort,
"jetty.ssl.port=" + httpsPort
};

File war = distribution.resolveArtifact("org.eclipse.jetty.demos:demo-simple-webapp:war:" + jettyVersion);
distribution.installWarFile(war, "demo");

try (JettyHomeTester.Run runStart = distribution.start(argsStart))
{
assertTrue(runStart.awaitConsoleLogsFor("Started Server@", 20, TimeUnit.SECONDS));

startHttpClient();
ContentResponse response = client.GET("http://localhost:" + httpPort + "/demo/index.html");
assertEquals(HttpStatus.OK_200, response.getStatus(), new ResponseDetails(response));
assertThat("Ensure that gzip is working", response.getHeaders().get(HttpHeader.CONTENT_ENCODING), containsString("gzip"));
}
}
}

@Test
public void testGzipExcludeMimeType() throws Exception
{
Path jettyBase = newTestJettyBaseDirectory();
String jettyVersion = System.getProperty("jettyVersion");
JettyHomeTester distribution = JettyHomeTester.Builder.newInstance()
.jettyVersion(jettyVersion)
.jettyBase(jettyBase)
.mavenLocalRepository(System.getProperty("mavenRepoPath"))
.build();

int httpPort = distribution.freePort();
int httpsPort = distribution.freePort();
assertThat("httpPort != httpsPort", httpPort, is(not(httpsPort)));

String[] argsConfig = {
"--add-modules=gzip",
"--add-modules=deploy,webapp,http"
};

try (JettyHomeTester.Run runConfig = distribution.start(argsConfig))
{
assertTrue(runConfig.awaitFor(5, TimeUnit.SECONDS));
assertEquals(0, runConfig.getExitValue());

String[] argsStart = {
"jetty.http.port=" + httpPort,
"jetty.httpConfig.port=" + httpsPort,
"jetty.ssl.port=" + httpsPort,
"jetty.gzip.excludedMimeTypeList=image/webp"
};

File war = distribution.resolveArtifact("org.eclipse.jetty.demos:demo-simple-webapp:war:" + jettyVersion);
distribution.installWarFile(war, "demo");

try (JettyHomeTester.Run runStart = distribution.start(argsStart))
{
assertTrue(runStart.awaitConsoleLogsFor("Started Server@", 20, TimeUnit.SECONDS));

startHttpClient();
ContentResponse response = client.GET("http://localhost:" + httpPort + "/demo/jetty.webp");
assertEquals(HttpStatus.OK_200, response.getStatus(), new ResponseDetails(response));
assertThat("Ensure that gzip exclusion worked", response.getHeaders().get(HttpHeader.CONTENT_ENCODING), not(containsString("gzip")));
}
}
}

private static class ResponseDetails implements Supplier<String>
{
private final ContentResponse response;

public ResponseDetails(ContentResponse response)
{
this.response = response;
}

@Override
public String get()
{
StringBuilder ret = new StringBuilder();
ret.append(response.toString()).append(System.lineSeparator());
ret.append(response.getHeaders().toString()).append(System.lineSeparator());
ret.append(response.getContentAsString()).append(System.lineSeparator());
return ret.toString();
}
}
}

0 comments on commit 1bdb69d

Please sign in to comment.