From dde194e2b9a57c1269104a0c9a26f975a53a010f Mon Sep 17 00:00:00 2001 From: Ivan Sopov Date: Sat, 20 Jun 2015 17:36:32 +0300 Subject: [PATCH] Use native compression for all embedded containers, not just Tomcat See gh-3296 --- .../autoconfigure/web/ServerProperties.java | 13 ++- .../web/ServerPropertiesTests.java | 13 +++ spring-boot/pom.xml | 5 ++ ...tConfigurableEmbeddedServletContainer.java | 51 ++++++++++++ .../JettyEmbeddedServletContainerFactory.java | 62 +++++++++++++- ...TomcatEmbeddedServletContainerFactory.java | 13 +++ .../UndertowEmbeddedServletContainer.java | 40 ++++++++- ...dertowEmbeddedServletContainerFactory.java | 4 +- ...tEmbeddedServletContainerFactoryTests.java | 82 +++++++++++++++++++ ...yEmbeddedServletContainerFactoryTests.java | 42 ++++++++++ 10 files changed, 320 insertions(+), 5 deletions(-) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java index cb5abd2057..38a74dc08b 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java @@ -31,6 +31,8 @@ import org.apache.catalina.valves.RemoteIpValve; import org.apache.coyote.AbstractProtocol; import org.apache.coyote.ProtocolHandler; import org.apache.coyote.http11.AbstractHttp11Protocol; +import org.springframework.boot.context.embedded.AbstractConfigurableEmbeddedServletContainer; +import org.springframework.boot.context.embedded.AbstractConfigurableEmbeddedServletContainer.CompressionProperties; import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer; import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer; import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizerBeanPostProcessor; @@ -99,6 +101,8 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer, Ord private final Undertow undertow = new Undertow(); + private CompressionProperties compression = new CompressionProperties(); + @NestedConfigurationProperty private JspServlet jspServlet; @@ -120,6 +124,10 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer, Ord return this.undertow; } + public CompressionProperties getCompression() { + return this.compression; + } + public String getContextPath() { return this.contextPath; } @@ -239,6 +247,10 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer, Ord if (getJspServlet() != null) { container.setJspServlet(getJspServlet()); } + if (container instanceof AbstractConfigurableEmbeddedServletContainer) { + ((AbstractConfigurableEmbeddedServletContainer) container) + .setCompression(getCompression()); + } if (container instanceof TomcatEmbeddedServletContainerFactory) { getTomcat() .customizeTomcat((TomcatEmbeddedServletContainerFactory) container); @@ -587,7 +599,6 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer, Ord valve.setSuffix(".log"); factory.addContextValves(valve); } - } public static class Undertow { diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java index e0a0023132..962eb74f52 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java @@ -37,6 +37,7 @@ import static org.hamcrest.core.IsInstanceOf.instanceOf; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -105,6 +106,18 @@ public class ServerPropertiesTests { .getInternalProxies()); } + @Test + public void testCompressionBinding() throws Exception { + Map map = new HashMap(); + map.put("server.compression.enabled", "true"); + map.put("server.compression.mimeTypes", "foo/bar"); + map.put("server.compression.minSize", "228"); + bindProperties(map); + assertTrue(this.properties.getCompression().isEnabled()); + assertEquals("foo/bar", this.properties.getCompression().getMimeTypes()); + assertEquals(228, this.properties.getCompression().getMinSize()); + } + @Test public void testCustomizeTomcat() throws Exception { ConfigurableEmbeddedServletContainer factory = mock(ConfigurableEmbeddedServletContainer.class); diff --git a/spring-boot/pom.xml b/spring-boot/pom.xml index 2780f4f475..08311a71e9 100644 --- a/spring-boot/pom.xml +++ b/spring-boot/pom.xml @@ -154,6 +154,11 @@ jetty-util true + + org.eclipse.jetty + jetty-servlets + true + io.undertow undertow-servlet diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/AbstractConfigurableEmbeddedServletContainer.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/AbstractConfigurableEmbeddedServletContainer.java index dfe985fc71..db8b75893b 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/AbstractConfigurableEmbeddedServletContainer.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/AbstractConfigurableEmbeddedServletContainer.java @@ -23,6 +23,7 @@ import java.util.Arrays; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; +import java.util.StringTokenizer; import java.util.concurrent.TimeUnit; import org.springframework.util.Assert; @@ -67,6 +68,8 @@ public abstract class AbstractConfigurableEmbeddedServletContainer implements private JspServlet jspServlet = new JspServlet(); + private CompressionProperties compression; + /** * Create a new {@link AbstractConfigurableEmbeddedServletContainer} instance. */ @@ -278,6 +281,14 @@ public abstract class AbstractConfigurableEmbeddedServletContainer implements return this.jspServlet; } + public CompressionProperties getCompression() { + return this.compression; + } + + public void setCompression(CompressionProperties compression) { + this.compression = compression; + } + /** * Utility method that can be used by subclasses wishing to combine the specified * {@link ServletContextInitializer} parameters with those defined in this instance. @@ -306,4 +317,44 @@ public abstract class AbstractConfigurableEmbeddedServletContainer implements .getClassLoader()); } + public static class CompressionProperties { + private boolean enabled = false; + private String mimeTypes = "text/html,text/xml,text/plain,text/css"; + private int minSize = 2048; + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public String getMimeTypes() { + return mimeTypes; + } + + public void setMimeTypes(String mimeTypes) { + this.mimeTypes = mimeTypes; + } + + public int getMinSize() { + return minSize; + } + + public void setMinSize(int minSize) { + this.minSize = minSize; + } + + public List getMimeTypesList() { + List mimeTypesList = new ArrayList(); + StringTokenizer tok = new StringTokenizer(mimeTypes, ",", false); + while (tok.hasMoreTokens()) { + mimeTypesList.add(tok.nextToken()); + } + return mimeTypesList; + } + + } + } diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactory.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactory.java index a7bf274f0a..c40655a331 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactory.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactory.java @@ -18,6 +18,7 @@ package org.springframework.boot.context.embedded.jetty; import java.io.File; import java.io.IOException; +import java.lang.reflect.Method; import java.net.InetSocketAddress; import java.net.URL; import java.util.ArrayList; @@ -37,6 +38,7 @@ import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.SessionManager; import org.eclipse.jetty.server.SslConnectionFactory; import org.eclipse.jetty.server.handler.ErrorHandler; +import org.eclipse.jetty.server.handler.HandlerWrapper; import org.eclipse.jetty.servlet.ErrorPageErrorHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletMapping; @@ -59,6 +61,7 @@ import org.springframework.context.ResourceLoaderAware; import org.springframework.core.io.ResourceLoader; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; +import org.springframework.util.ReflectionUtils; import org.springframework.util.ResourceUtils; import org.springframework.util.StringUtils; @@ -113,6 +116,12 @@ public class JettyEmbeddedServletContainerFactory extends super(contextPath, port); } + private static final String[] GZIP_HANDLER_CLASSNAMES = new String[] { + "org.eclipse.jetty.servlets.gzip.GzipHandler", // Jetty 9.2 + "org.eclipse.jetty.server.handler.gzip.GzipHandler", // Jetty 9.3 + "org.eclipse.jetty.server.handler.GzipHandler" // Jetty 8 + }; + @Override public EmbeddedServletContainer getEmbeddedServletContainer( ServletContextInitializer... initializers) { @@ -120,7 +129,12 @@ public class JettyEmbeddedServletContainerFactory extends int port = (getPort() >= 0 ? getPort() : 0); Server server = new Server(new InetSocketAddress(getAddress(), port)); configureWebAppContext(context, initializers); - server.setHandler(context); + if (getCompression() != null && getCompression().isEnabled()) { + setupGzipHandler(context, server); + } + else { + server.setHandler(context); + } this.logger.info("Server initialized with port: " + port); if (getSsl() != null && getSsl().isEnabled()) { SslContextFactory sslContextFactory = new SslContextFactory(); @@ -135,6 +149,52 @@ public class JettyEmbeddedServletContainerFactory extends return getJettyEmbeddedServletContainer(server); } + private void setupGzipHandler(JettyEmbeddedWebAppContext context, Server server) + throws LinkageError { + boolean done = false; + for (String gzipHandlerClassName : GZIP_HANDLER_CLASSNAMES) { + if (ClassUtils.isPresent(gzipHandlerClassName, null)) { + try { + Class gzipHandlerClass = ClassUtils.forName(gzipHandlerClassName, + null); + + HandlerWrapper gzipHandler = (HandlerWrapper) gzipHandlerClass + .newInstance(); + gzipHandler.setHandler(context); + + Method minGzipSizeMethod = ReflectionUtils.findMethod( + gzipHandlerClass, "setMinGzipSize", int.class); + minGzipSizeMethod.invoke(gzipHandler, getCompression().getMinSize()); + + Method mimeTypesMethod = ReflectionUtils.findMethod(gzipHandlerClass, + "setMimeTypes", String.class); + if (mimeTypesMethod != null) { + // Jetty 8 & Jety 9.2 + mimeTypesMethod.invoke(gzipHandler, getCompression() + .getMimeTypes()); + } + else { + // Jetty 9.3 + mimeTypesMethod = ReflectionUtils.findMethod(gzipHandlerClass, + "setIncludedMimeTypes", String[].class); + List mimeTypes = getCompression().getMimeTypesList(); + mimeTypesMethod.invoke(gzipHandler, + (Object) mimeTypes.toArray(new String[mimeTypes.size()])); + } + server.setHandler(gzipHandler); + done = true; + break; + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + } + if (!done) { + throw new IllegalStateException("Jetty GzipHandler is not in classpath"); + } + } + private SslServerConnectorFactory getSslServerConnectorFactory() { if (ClassUtils.isPresent("org.eclipse.jetty.server.ssl.SslSocketConnector", null)) { return new Jetty8SslServerConnectorFactory(); diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java index 96d9d1f750..9045648a15 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java @@ -46,7 +46,9 @@ import org.apache.catalina.loader.WebappLoader; import org.apache.catalina.startup.Tomcat; import org.apache.catalina.startup.Tomcat.FixContextListener; import org.apache.coyote.AbstractProtocol; +import org.apache.coyote.ProtocolHandler; import org.apache.coyote.http11.AbstractHttp11JsseProtocol; +import org.apache.coyote.http11.AbstractHttp11Protocol; import org.springframework.beans.BeanUtils; import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.EmbeddedServletContainer; @@ -255,6 +257,17 @@ public class TomcatEmbeddedServletContainerFactory extends connector.setSecure(true); } + if (getCompression() != null && getCompression().isEnabled()) { + ProtocolHandler handler = connector.getProtocolHandler(); + if (handler instanceof AbstractHttp11Protocol) { + @SuppressWarnings("rawtypes") + AbstractHttp11Protocol protocol = (AbstractHttp11Protocol) handler; + protocol.setCompression("on"); + protocol.setCompressionMinSize(getCompression().getMinSize()); + protocol.setCompressableMimeTypes(getCompression().getMimeTypes()); + } + } + for (TomcatConnectorCustomizer customizer : this.tomcatConnectorCustomizers) { customizer.customize(connector); } diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainer.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainer.java index fd2c8928e3..82dfe4583a 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainer.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainer.java @@ -19,8 +19,17 @@ package org.springframework.boot.context.embedded.undertow; import io.undertow.Handlers; import io.undertow.Undertow; import io.undertow.Undertow.Builder; +import io.undertow.attribute.ConstantExchangeAttribute; +import io.undertow.attribute.ExchangeAttribute; +import io.undertow.attribute.ResponseHeaderAttribute; +import io.undertow.predicate.Predicate; +import io.undertow.predicate.Predicates; import io.undertow.server.HttpHandler; +import io.undertow.server.handlers.encoding.ContentEncodingRepository; +import io.undertow.server.handlers.encoding.EncodingHandler; +import io.undertow.server.handlers.encoding.GzipEncodingProvider; import io.undertow.servlet.api.DeploymentManager; +import io.undertow.util.HttpString; import java.lang.reflect.Field; import java.net.ServerSocket; @@ -31,8 +40,10 @@ import javax.servlet.ServletException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.boot.context.embedded.AbstractConfigurableEmbeddedServletContainer.CompressionProperties; import org.springframework.boot.context.embedded.EmbeddedServletContainer; import org.springframework.boot.context.embedded.EmbeddedServletContainerException; +import org.springframework.http.HttpHeaders; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; @@ -59,16 +70,20 @@ public class UndertowEmbeddedServletContainer implements EmbeddedServletContaine private final boolean autoStart; + private final CompressionProperties compression; + private Undertow undertow; private boolean started = false; public UndertowEmbeddedServletContainer(Builder builder, DeploymentManager manager, - String contextPath, int port, boolean autoStart) { + String contextPath, int port, boolean autoStart, + CompressionProperties compression) { this.builder = builder; this.manager = manager; this.contextPath = contextPath; this.autoStart = autoStart; + this.compression = compression; } @Override @@ -98,6 +113,29 @@ public class UndertowEmbeddedServletContainer implements EmbeddedServletContaine } private HttpHandler getContextHandler(HttpHandler servletHandler) { + if (compression != null && compression.isEnabled()) { + ContentEncodingRepository encodingRepository = new ContentEncodingRepository(); + + List mimeTypes = compression.getMimeTypesList(); + Predicate[] mimePredicates = new Predicate[mimeTypes.size()]; + ResponseHeaderAttribute mimeHeader = new ResponseHeaderAttribute( + new HttpString(HttpHeaders.CONTENT_TYPE)); + for (int i = 0; i < mimeTypes.size(); i++) { + mimePredicates[i] = Predicates.equals(new ExchangeAttribute[] { + mimeHeader, new ConstantExchangeAttribute(mimeTypes.get(i)) }); + + } + + Predicate mimeAndSizePredicate = Predicates.and( + Predicates.maxContentSize(compression.getMinSize()), + Predicates.or(mimePredicates)); + + encodingRepository.addEncodingHandler("gzip", new GzipEncodingProvider(), 50, + mimeAndSizePredicate); + servletHandler = new EncodingHandler(encodingRepository) + .setNext(servletHandler); + } + if (StringUtils.isEmpty(this.contextPath)) { return servletHandler; } diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactory.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactory.java index 2fb0abde8d..6fef6b9ba8 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactory.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactory.java @@ -219,7 +219,7 @@ public class UndertowEmbeddedServletContainerFactory extends int port = getPort(); Builder builder = createBuilder(port); return new UndertowEmbeddedServletContainer(builder, manager, getContextPath(), - port, port >= 0); + port, port >= 0, getCompression()); } private Builder createBuilder(int port) { @@ -470,7 +470,7 @@ public class UndertowEmbeddedServletContainerFactory extends protected UndertowEmbeddedServletContainer getUndertowEmbeddedServletContainer( Builder builder, DeploymentManager manager, int port) { return new UndertowEmbeddedServletContainer(builder, manager, getContextPath(), - port, port >= 0); + port, port >= 0, getCompression()); } @Override diff --git a/spring-boot/src/test/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactoryTests.java b/spring-boot/src/test/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactoryTests.java index 30a3be1bee..9ff051ace4 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactoryTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/embedded/AbstractEmbeddedServletContainerFactoryTests.java @@ -20,6 +20,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; +import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.Charset; @@ -27,6 +28,8 @@ import java.security.KeyStore; import java.util.Arrays; import java.util.Date; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.zip.GZIPInputStream; import javax.net.ssl.SSLException; import javax.servlet.GenericServlet; @@ -36,8 +39,10 @@ import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import org.apache.http.client.HttpClient; +import org.apache.http.client.entity.InputStreamFactory; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.TrustSelfSignedStrategy; +import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; import org.apache.http.ssl.SSLContextBuilder; import org.junit.After; @@ -46,6 +51,7 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import org.mockito.InOrder; +import org.springframework.boot.context.embedded.AbstractConfigurableEmbeddedServletContainer.CompressionProperties; import org.springframework.boot.context.embedded.Ssl.ClientAuth; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; @@ -58,6 +64,7 @@ import org.springframework.util.SocketUtils; import org.springframework.util.StreamUtils; import org.springframework.util.concurrent.ListenableFuture; +import static java.util.Collections.singletonMap; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; @@ -65,7 +72,9 @@ import static org.hamcrest.Matchers.lessThan; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.anyObject; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; @@ -520,6 +529,79 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests { assertThat(getFactory().getSessionTimeout(), equalTo(30 * 60)); } + @Test + public void compression() throws Exception { + assertTrue(internalTestCompression(10000, null)); + } + + @Test + public void noCompressionForSmallResponse() throws Exception { + assertFalse(internalTestCompression(100, null)); + } + + @Test + public void noCompressionForMimeType() throws Exception { + assertFalse(internalTestCompression(10000, "text/html,text/xml,text/css")); + } + + protected String setupFactoryForCompression(int contentSize, String mimeTypes) + throws Exception { + char[] chars = new char[contentSize]; + Arrays.fill(chars, 'F'); + String testContent = new String(chars); + + AbstractEmbeddedServletContainerFactory factory = getFactory(); + + FileCopyUtils.copy(testContent, + new FileWriter(this.temporaryFolder.newFile("test.txt"))); + factory.setDocumentRoot(this.temporaryFolder.getRoot()); + CompressionProperties compression = new CompressionProperties(); + compression.setEnabled(true); + if (mimeTypes != null) { + compression.setMimeTypes(mimeTypes); + } + factory.setCompression(compression); + + this.container = factory.getEmbeddedServletContainer(); + this.container.start(); + return testContent; + } + + private boolean internalTestCompression(int contentSize, String mimeTypes) + throws Exception { + String testContent = setupFactoryForCompression(contentSize, mimeTypes); + + class TestGzipInputStreamFactory implements InputStreamFactory { + + final AtomicBoolean requested = new AtomicBoolean(false); + + @Override + public InputStream create(InputStream instream) throws IOException { + if (requested.get()) { + throw new IllegalStateException( + "On deflated InputStream already requested"); + } + requested.set(true); + return new GZIPInputStream(instream); + } + + } + + TestGzipInputStreamFactory gzipTestInputStreamFactory = new TestGzipInputStreamFactory(); + + String response = getResponse( + getLocalUrl("/test.txt"), + new HttpComponentsClientHttpRequestFactory(HttpClientBuilder + .create() + .setContentDecoderRegistry( + singletonMap("gzip", + (InputStreamFactory) gzipTestInputStreamFactory)) + .build())); + assertThat(response, equalTo(testContent)); + boolean wasCompressionUsed = gzipTestInputStreamFactory.requested.get(); + return wasCompressionUsed; + } + private void addTestTxtFile(AbstractEmbeddedServletContainerFactory factory) throws IOException { FileCopyUtils.copy("test", diff --git a/spring-boot/src/test/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactoryTests.java b/spring-boot/src/test/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactoryTests.java index cffab998ba..270dc0a22b 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactoryTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/embedded/jetty/JettyEmbeddedServletContainerFactoryTests.java @@ -16,11 +16,17 @@ package org.springframework.boot.context.embedded.jetty; +import java.io.IOException; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; @@ -32,8 +38,12 @@ import org.eclipse.jetty.webapp.Configuration; import org.eclipse.jetty.webapp.WebAppContext; import org.junit.Test; import org.mockito.InOrder; +import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactoryTests; +import org.springframework.boot.context.embedded.ServletRegistrationBean; import org.springframework.boot.context.embedded.Ssl; +import org.springframework.boot.context.embedded.AbstractConfigurableEmbeddedServletContainer.CompressionProperties; +import org.springframework.http.HttpHeaders; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; @@ -173,6 +183,38 @@ public class JettyEmbeddedServletContainerFactoryTests extends assertThat(getJspServlet().getInitParameters(), is(equalTo(initParameters))); } + @Override + @SuppressWarnings("serial") + // work-around for Jetty issue - https://bugs.eclipse.org/bugs/show_bug.cgi?id=470646 + protected String setupFactoryForCompression(final int contentSize, String mimeTypes) + throws Exception { + char[] chars = new char[contentSize]; + Arrays.fill(chars, 'F'); + final String testContent = new String(chars); + + AbstractEmbeddedServletContainerFactory factory = getFactory(); + + CompressionProperties compression = new CompressionProperties(); + compression.setEnabled(true); + if (mimeTypes != null) { + compression.setMimeTypes(mimeTypes); + } + factory.setCompression(compression); + + this.container = factory.getEmbeddedServletContainer(new ServletRegistrationBean( + new HttpServlet() { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + resp.setContentLength(contentSize); + resp.setHeader(HttpHeaders.CONTENT_TYPE, "text/plain"); + resp.getWriter().print(testContent); + } + }, "/test.txt")); + this.container.start(); + return testContent; + } + @Override protected ServletHolder getJspServlet() { WebAppContext context = (WebAppContext) ((JettyEmbeddedServletContainer) this.container)