From 334e02833a2c5ed50832caf689ccf68b9ba44acc Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Wed, 16 Feb 2022 12:57:17 +1100 Subject: [PATCH] Issue #7567 - don't compare params when checking MIME type for GzipHandler Signed-off-by: Lachlan Roberts --- .../server/handler/gzip/GzipHandler.java | 2 +- .../gzip/GzipHttpOutputInterceptor.java | 6 +- .../jetty/servlet/MultiPartServletTest.java | 87 +++++++++++++++++-- 3 files changed, 85 insertions(+), 10 deletions(-) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java index c437d4f5f313..d7ced5a535a2 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHandler.java @@ -727,7 +727,7 @@ public void handle(String target, Request baseRequest, HttpServletRequest reques String mimeType = context == null ? MimeTypes.getDefaultMimeByExtension(path) : context.getMimeType(path); if (mimeType != null) { - mimeType = MimeTypes.getContentTypeWithoutCharset(mimeType); + mimeType = HttpFields.valueParameters(mimeType, null); if (!isMimeTypeGzipable(mimeType)) { LOG.debug("{} excluded by path suffix mime type {}", this, request); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHttpOutputInterceptor.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHttpOutputInterceptor.java index fa007a43c428..703db535586e 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHttpOutputInterceptor.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/gzip/GzipHttpOutputInterceptor.java @@ -28,7 +28,6 @@ import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpStatus; -import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.http.PreEncodedHttpField; import org.eclipse.jetty.server.HttpChannel; import org.eclipse.jetty.server.HttpOutput; @@ -36,7 +35,6 @@ import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.IteratingNestedCallback; -import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -171,8 +169,8 @@ protected void commit(ByteBuffer content, boolean complete, Callback callback) String ct = response.getContentType(); if (ct != null) { - ct = MimeTypes.getContentTypeWithoutCharset(ct); - if (!_factory.isMimeTypeGzipable(StringUtil.asciiToLowerCase(ct))) + String baseType = HttpFields.valueParameters(ct, null); + if (!_factory.isMimeTypeGzipable(baseType)) { LOG.debug("{} exclude by mimeType {}", this, ct); noCompression(); diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/MultiPartServletTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/MultiPartServletTest.java index d0297e1cd51c..b87e63c90785 100644 --- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/MultiPartServletTest.java +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/MultiPartServletTest.java @@ -19,10 +19,15 @@ package org.eclipse.jetty.servlet; import java.io.IOException; +import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; import java.util.stream.Stream; +import java.util.zip.GZIPInputStream; import javax.servlet.MultipartConfigElement; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -32,8 +37,13 @@ import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.util.BytesContentProvider; +import org.eclipse.jetty.client.util.InputStreamResponseListener; import org.eclipse.jetty.client.util.MultiPartContentProvider; +import org.eclipse.jetty.client.util.StringContentProvider; +import org.eclipse.jetty.http.HttpFields; +import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.MimeTypes; @@ -43,12 +53,14 @@ import org.eclipse.jetty.server.MultiPartFormDataCompliance; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.handler.gzip.GzipHandler; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.StacklessLogging; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -56,7 +68,9 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.startsWith; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; public class MultiPartServletTest { @@ -96,29 +110,54 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws S } } + public static class MultiPartEchoServlet extends HttpServlet + { + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + if (!req.getContentType().contains(MimeTypes.Type.MULTIPART_FORM_DATA.asString())) + { + resp.sendError(400); + return; + } + + resp.setContentType(req.getContentType()); + IO.copy(req.getInputStream(), resp.getOutputStream()); + } + } + @BeforeEach public void start() throws Exception { tmpDir = Files.createTempDirectory(MultiPartServletTest.class.getSimpleName()); + assertNotNull(tmpDir); server = new Server(); connector = new ServerConnector(server); server.addConnector(connector); + MultipartConfigElement config = new MultipartConfigElement(tmpDir.toAbsolutePath().toString(), + MAX_FILE_SIZE, -1, 1); + ServletContextHandler contextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS); contextHandler.setContextPath("/"); ServletHolder servletHolder = contextHandler.addServlet(MultiPartServlet.class, "/"); - - MultipartConfigElement config = new MultipartConfigElement(tmpDir.toAbsolutePath().toString(), - MAX_FILE_SIZE, -1, 1); + servletHolder.getRegistration().setMultipartConfig(config); + servletHolder = contextHandler.addServlet(MultiPartEchoServlet.class, "/echo"); servletHolder.getRegistration().setMultipartConfig(config); - server.setHandler(contextHandler); + GzipHandler gzipHandler = new GzipHandler(); + gzipHandler.addIncludedMethods(HttpMethod.POST.asString()); + gzipHandler.addIncludedMimeTypes("multipart/form-data"); + gzipHandler.setMinGzipSize(32); + gzipHandler.setHandler(contextHandler); + server.setHandler(gzipHandler); server.start(); client = new HttpClient(); client.start(); + client.getContentDecoderFactories().clear(); } @AfterEach @@ -161,6 +200,44 @@ public void testTempFilesDeletedOnError(MultiPartFormDataCompliance compliance) containsString("Multipart Mime part largePart exceeds max filesize")); } - assertThat(tmpDir.toFile().list().length, is(0)); + String[] fileList = tmpDir.toFile().list(); + assertNotNull(fileList); + assertThat(fileList.length, is(0)); + } + + @Test + public void testMultiPartGzip() throws Exception + { + String contentString = "the quick brown fox jumps over the lazy dog, " + + "the quick brown fox jumps over the lazy dog"; + StringContentProvider content = new StringContentProvider(contentString); + + MultiPartContentProvider multiPart = new MultiPartContentProvider(); + multiPart.addFieldPart("largePart", content, null); + multiPart.close(); + + try (StacklessLogging ignored = new StacklessLogging(HttpChannel.class, MultiPartFormInputStream.class)) + { + InputStreamResponseListener responseStream = new InputStreamResponseListener(); + client.newRequest("localhost", connector.getLocalPort()) + .path("/echo") + .scheme(HttpScheme.HTTP.asString()) + .method(HttpMethod.POST) + .header(HttpHeader.ACCEPT_ENCODING, "gzip") + .content(multiPart) + .send(responseStream); + + Response response = responseStream.get(5, TimeUnit.SECONDS); + HttpFields headers = response.getHeaders(); + assertThat(headers.get(HttpHeader.CONTENT_TYPE), startsWith("multipart/form-data")); + assertThat(headers.get(HttpHeader.CONTENT_ENCODING), is("gzip")); + + InputStream inputStream = new GZIPInputStream(responseStream.getInputStream()); + String contentType = headers.get(HttpHeader.CONTENT_TYPE); + MultiPartFormInputStream mpis = new MultiPartFormInputStream(inputStream, contentType, null, null); + List parts = new ArrayList<>(mpis.getParts()); + assertThat(parts.size(), is(1)); + assertThat(IO.toString(parts.get(0).getInputStream()), is(contentString)); + } } }