Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue #6544 - Fixing broken jetty.gzip.excludedMimeTypeList property support #6550

Merged
merged 6 commits into from Jul 30, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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>

joakime marked this conversation as resolved.
Show resolved Hide resolved
</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"/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If these methods did not exist, then the changes from #6398 should have detected that???

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah they did exist, they just had the wrong signature.

<Set name="includedMimeTypesList" property="jetty.gzip.includedMimeTypeList"/>
<Set name="excludedMimeTypesList" property="jetty.gzip.excludedMimeTypeList"/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have preferred to split the string in the XML, but we can't if we maintain the property attribute ability to not set if not set.

<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;
}
joakime marked this conversation as resolved.
Show resolved Hide resolved
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)));
joakime marked this conversation as resolved.
Show resolved Hide resolved

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
joakime marked this conversation as resolved.
Show resolved Hide resolved
};

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)));
joakime marked this conversation as resolved.
Show resolved Hide resolved

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,
joakime marked this conversation as resolved.
Show resolved Hide resolved
"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>
joakime marked this conversation as resolved.
Show resolved Hide resolved
{
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();
}
}
}