Replace GzipFilter and Tomcat compression with general purpose approach

Closes gh-3296
This commit is contained in:
Andy Wilkinson 2015-06-29 14:50:48 +01:00
parent dde194e2b9
commit 00d594dcda
27 changed files with 414 additions and 771 deletions

View File

@ -200,11 +200,6 @@
<artifactId>HikariCP-java6</artifactId> <artifactId>HikariCP-java6</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlets</artifactId>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId> <artifactId>jetty-webapp</artifactId>

View File

@ -1,53 +0,0 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.web;
import org.eclipse.jetty.servlets.GzipFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* {@link EnableAutoConfiguration Auto-configuration} for {@link GzipFilter}.
*
* @author Andy Wilkinson
* @since 1.2.2
*/
@Configuration
@ConditionalOnClass(GzipFilter.class)
@EnableConfigurationProperties(GzipFilterProperties.class)
public class GzipFilterAutoConfiguration {
@Autowired
private GzipFilterProperties properties;
@Bean
@ConditionalOnProperty(prefix = "spring.http.gzip", name = "enabled", matchIfMissing = true)
public FilterRegistrationBean gzipFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean(new GzipFilter());
registration.addUrlPatterns("/*");
registration.setInitParameters(this.properties.getAsInitParameters());
return registration;
}
}

View File

@ -1,242 +0,0 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.web;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.jetty.servlets.GzipFilter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.http.HttpMethod;
import org.springframework.util.MimeType;
import org.springframework.util.StringUtils;
/**
* Properties for configuring {@link GzipFilter}.
*
* @author Andy Wilkinson
* @author Stephane Nicoll
* @since 1.2.2
*/
@ConfigurationProperties(prefix = "spring.http.gzip")
public class GzipFilterProperties {
private final Map<String, String> initParameters = new HashMap<String, String>();
/**
* Size of the output buffer in bytes.
*/
private Integer bufferSize;
/**
* Minimum content length required for compression to occur.
*/
private Integer minGzipSize;
/**
* Level used for deflate compression (0-9).
*/
private Integer deflateCompressionLevel;
/**
* noWrap setting for deflate compression.
*/
private Boolean deflateNoWrap;
/**
* Comma-separated list of HTTP methods for which compression is enabled.
*/
private List<HttpMethod> methods;
/**
* Comma-separated list of MIME types which should be compressed.
*/
private List<MimeType> mimeTypes;
/**
* Comma-separated list of MIME types to exclude from compression.
*/
private List<MimeType> excludedMimeTypes;
/**
* Comma-separated list of user agents to exclude from compression. String.contains is
* used to determine a match against the request's User-Agent header.
*/
private String excludedAgents;
/**
* Comma-separated list of regular expression patterns to control user agents excluded
* from compression.
*/
private String excludeAgentPatterns;
/**
* Comma-separated list of paths to exclude from compression. Uses String.startsWith
* to determine a match against the request's path.
*/
private String excludePaths;
/**
* Comma-separated list of regular expression patterns to control the paths that are
* excluded from compression.
*/
private String excludePathPatterns;
/**
* Vary header sent on responses that may be compressed.
*/
private String vary;
public GzipFilterProperties() {
this.addInitParameter("checkGzExists", false);
}
public Integer getBufferSize() {
return this.bufferSize;
}
public void setBufferSize(Integer bufferSize) {
this.addInitParameter("bufferSize", bufferSize);
this.bufferSize = bufferSize;
}
public Integer getMinGzipSize() {
return this.minGzipSize;
}
public void setMinGzipSize(Integer minGzipSize) {
this.addInitParameter("minGzipSize", minGzipSize);
this.minGzipSize = minGzipSize;
}
public Integer getDeflateCompressionLevel() {
return this.deflateCompressionLevel;
}
public void setDeflateCompressionLevel(Integer deflateCompressionLevel) {
this.addInitParameter("deflateCompressionLevel", deflateCompressionLevel);
this.deflateCompressionLevel = deflateCompressionLevel;
}
public Boolean getDeflateNoWrap() {
return this.deflateNoWrap;
}
public void setDeflateNoWrap(Boolean deflateNoWrap) {
this.addInitParameter("deflateNoWrap", deflateNoWrap);
this.deflateNoWrap = deflateNoWrap;
}
public List<HttpMethod> getMethods() {
return this.methods;
}
public void setMethods(List<HttpMethod> methods) {
this.addInitParameter("methods",
StringUtils.collectionToCommaDelimitedString(methods));
this.methods = methods;
}
public List<MimeType> getMimeTypes() {
return this.mimeTypes;
}
public void setMimeTypes(List<MimeType> mimeTypes) {
this.addInitParameter("mimeTypes",
StringUtils.collectionToCommaDelimitedString(mimeTypes));
this.mimeTypes = mimeTypes;
}
public List<MimeType> getExcludedMimeTypes() {
return this.excludedMimeTypes;
}
public void setExcludedMimeTypes(List<MimeType> excludedMimeTypes) {
this.addInitParameter("excludedMimeTypes",
StringUtils.collectionToCommaDelimitedString(excludedMimeTypes));
this.excludedMimeTypes = excludedMimeTypes;
}
public String getExcludedAgents() {
return this.excludedAgents;
}
public void setExcludedAgents(String excludedAgents) {
this.addInitParameter("excludedAgents", excludedAgents);
this.excludedAgents = excludedAgents;
}
public String getExcludeAgentPatterns() {
return this.excludeAgentPatterns;
}
public void setExcludeAgentPatterns(String excludeAgentPatterns) {
this.addInitParameter("excludeAgentPatterns", excludeAgentPatterns);
this.excludeAgentPatterns = excludeAgentPatterns;
}
public String getExcludePaths() {
return this.excludePaths;
}
public void setExcludePaths(String excludePaths) {
this.addInitParameter("excludePaths", excludePaths);
this.excludePaths = excludePaths;
}
public String getExcludePathPatterns() {
return this.excludePathPatterns;
}
public void setExcludePathPatterns(String excludePathPatterns) {
this.addInitParameter("excludePathPatterns", excludePathPatterns);
this.excludePathPatterns = excludePathPatterns;
}
public String getVary() {
return this.vary;
}
public void setVary(String vary) {
this.addInitParameter("vary", vary);
this.vary = vary;
}
Map<String, String> getAsInitParameters() {
return this.initParameters;
}
private void addInitParameter(String name, Integer value) {
if (value != null) {
this.initParameters.put(name, value.toString());
}
}
private void addInitParameter(String name, Boolean value) {
if (value != null) {
this.initParameters.put(name, value.toString());
}
}
private void addInitParameter(String name, String value) {
if (value != null) {
this.initParameters.put(name, value.toString());
}
}
}

View File

@ -31,8 +31,7 @@ import org.apache.catalina.valves.RemoteIpValve;
import org.apache.coyote.AbstractProtocol; import org.apache.coyote.AbstractProtocol;
import org.apache.coyote.ProtocolHandler; import org.apache.coyote.ProtocolHandler;
import org.apache.coyote.http11.AbstractHttp11Protocol; import org.apache.coyote.http11.AbstractHttp11Protocol;
import org.springframework.boot.context.embedded.AbstractConfigurableEmbeddedServletContainer; import org.springframework.boot.context.embedded.Compression;
import org.springframework.boot.context.embedded.AbstractConfigurableEmbeddedServletContainer.CompressionProperties;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer; import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer; import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizerBeanPostProcessor; import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizerBeanPostProcessor;
@ -101,7 +100,8 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer, Ord
private final Undertow undertow = new Undertow(); private final Undertow undertow = new Undertow();
private CompressionProperties compression = new CompressionProperties(); @NestedConfigurationProperty
private Compression compression = new Compression();
@NestedConfigurationProperty @NestedConfigurationProperty
private JspServlet jspServlet; private JspServlet jspServlet;
@ -124,7 +124,7 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer, Ord
return this.undertow; return this.undertow;
} }
public CompressionProperties getCompression() { public Compression getCompression() {
return this.compression; return this.compression;
} }
@ -247,9 +247,8 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer, Ord
if (getJspServlet() != null) { if (getJspServlet() != null) {
container.setJspServlet(getJspServlet()); container.setJspServlet(getJspServlet());
} }
if (container instanceof AbstractConfigurableEmbeddedServletContainer) { if (getCompression() != null) {
((AbstractConfigurableEmbeddedServletContainer) container) container.setCompression(getCompression());
.setCompression(getCompression());
} }
if (container instanceof TomcatEmbeddedServletContainerFactory) { if (container instanceof TomcatEmbeddedServletContainerFactory) {
getTomcat() getTomcat()
@ -359,19 +358,6 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer, Ord
*/ */
private String uriEncoding; private String uriEncoding;
/**
* Controls response compression. Acceptable values are "off" to disable
* compression, "on" to enable compression of responses over 2048 bytes, "force"
* to force response compression, or an integer value to enable compression of
* responses with content length that is at least that value.
*/
private String compression = "off";
/**
* Comma-separated list of MIME types for which compression is used.
*/
private String compressableMimeTypes = "text/html,text/xml,text/plain";
public int getMaxThreads() { public int getMaxThreads() {
return this.maxThreads; return this.maxThreads;
} }
@ -420,22 +406,6 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer, Ord
this.accessLogPattern = accessLogPattern; this.accessLogPattern = accessLogPattern;
} }
public String getCompressableMimeTypes() {
return this.compressableMimeTypes;
}
public void setCompressableMimeTypes(String compressableMimeTypes) {
this.compressableMimeTypes = compressableMimeTypes;
}
public String getCompression() {
return this.compression;
}
public void setCompression(String compression) {
this.compression = compression;
}
public String getInternalProxies() { public String getInternalProxies() {
return this.internalProxies; return this.internalProxies;
} }
@ -496,7 +466,6 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer, Ord
if (this.maxHttpHeaderSize > 0) { if (this.maxHttpHeaderSize > 0) {
customizeMaxHttpHeaderSize(factory); customizeMaxHttpHeaderSize(factory);
} }
customizeCompression(factory);
if (this.accessLogEnabled) { if (this.accessLogEnabled) {
customizeAccessLog(factory); customizeAccessLog(factory);
} }
@ -565,33 +534,6 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer, Ord
}); });
} }
private void customizeCompression(TomcatEmbeddedServletContainerFactory factory) {
factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {
@Override
public void customize(Connector connector) {
ProtocolHandler handler = connector.getProtocolHandler();
if (handler instanceof AbstractHttp11Protocol) {
@SuppressWarnings("rawtypes")
AbstractHttp11Protocol protocol = (AbstractHttp11Protocol) handler;
protocol.setCompression(coerceCompression(Tomcat.this.compression));
protocol.setCompressableMimeTypes(Tomcat.this.compressableMimeTypes);
}
}
private String coerceCompression(String compression) {
if ("true".equalsIgnoreCase(compression)) {
return "on";
}
if ("false".equalsIgnoreCase(compression)) {
return "off";
}
return compression;
}
});
}
private void customizeAccessLog(TomcatEmbeddedServletContainerFactory factory) { private void customizeAccessLog(TomcatEmbeddedServletContainerFactory factory) {
AccessLogValve valve = new AccessLogValve(); AccessLogValve valve = new AccessLogValve();
String accessLogPattern = getAccessLogPattern(); String accessLogPattern = getAccessLogPattern();

View File

@ -68,7 +68,6 @@ org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration,\ org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,\ org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.GzipFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration,\ org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration,\ org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration,\ org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration,\

View File

@ -1,123 +0,0 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.web;
import org.junit.After;
import org.junit.Test;
import org.springframework.boot.context.embedded.FilterRegistrationBean;
import org.springframework.boot.test.EnvironmentTestUtils;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
/**
* Tests for {@link GzipFilterAutoConfiguration}
*
* @author Andy Wilkinson
*/
public class GzipFilterAutoConfigurationTests {
private AnnotationConfigApplicationContext context;
@After
public void close() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void filterIsMappedToSlashStar() {
createAndRefreshContext();
FilterRegistrationBean registrationBean = this.context.getBean("gzipFilter",
FilterRegistrationBean.class);
assertThat(registrationBean.getUrlPatterns(), contains("/*"));
}
@Test
public void byDefaultCheckGzExistsIsTheOnlyInitParameter() {
createAndRefreshContext();
FilterRegistrationBean registrationBean = this.context.getBean("gzipFilter",
FilterRegistrationBean.class);
assertThat(registrationBean.getInitParameters().size(), equalTo(1));
assertThat(registrationBean.getInitParameters().get("checkGzExists"),
equalTo("false"));
}
@Test
public void customInitParameterConfiguration() {
createAndRefreshContext("spring.http.gzip.bufferSize:1234",
"spring.http.gzip.minGzipSize:2345",
"spring.http.gzip.deflateCompressionLevel:5",
"spring.http.gzip.deflateNoWrap:false",
"spring.http.gzip.methods:GET,POST",
"spring.http.gzip.mimeTypes:application/foo,application/bar",
"spring.http.gzip.excludedMimeTypes:application/biz",
"spring.http.gzip.excludedAgents:excluded-agent-1,excluded-agent-2",
"spring.http.gzip.excludeAgentPatterns:agent-pattern-1,agent-pattern-2",
"spring.http.gzip.excludePaths:/static/",
"spring.http.gzip.excludePathPatterns:path-pattern",
"spring.http.gzip.vary:vary-header-value");
FilterRegistrationBean registrationBean = this.context.getBean("gzipFilter",
FilterRegistrationBean.class);
assertThat(registrationBean.getInitParameters().size(), equalTo(13));
assertThat(registrationBean.getInitParameters().get("checkGzExists"),
equalTo("false"));
assertThat(registrationBean.getInitParameters().get("bufferSize"),
equalTo("1234"));
assertThat(registrationBean.getInitParameters().get("minGzipSize"),
equalTo("2345"));
assertThat(registrationBean.getInitParameters().get("deflateCompressionLevel"),
equalTo("5"));
assertThat(registrationBean.getInitParameters().get("deflateNoWrap"),
equalTo("false"));
assertThat(registrationBean.getInitParameters().get("methods"),
equalTo("GET,POST"));
assertThat(registrationBean.getInitParameters().get("mimeTypes"),
equalTo("application/foo,application/bar"));
assertThat(registrationBean.getInitParameters().get("excludedMimeTypes"),
equalTo("application/biz"));
assertThat(registrationBean.getInitParameters().get("excludedAgents"),
equalTo("excluded-agent-1,excluded-agent-2"));
assertThat(registrationBean.getInitParameters().get("excludeAgentPatterns"),
equalTo("agent-pattern-1,agent-pattern-2"));
assertThat(registrationBean.getInitParameters().get("excludePaths"),
equalTo("/static/"));
assertThat(registrationBean.getInitParameters().get("excludePathPatterns"),
equalTo("path-pattern"));
assertThat(registrationBean.getInitParameters().get("vary"),
equalTo("vary-header-value"));
}
@Test
public void filterCanBeDisabled() {
createAndRefreshContext("spring.http.gzip.enabled:false");
assertThat(this.context.getBeanNamesForType(FilterRegistrationBean.class).length,
is(equalTo(0)));
}
private void createAndRefreshContext(String... pairs) {
this.context = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context, pairs);
this.context.register(GzipFilterAutoConfiguration.class);
this.context.refresh();
}
}

View File

@ -23,21 +23,16 @@ import java.util.Map;
import org.apache.catalina.Valve; import org.apache.catalina.Valve;
import org.apache.catalina.valves.RemoteIpValve; import org.apache.catalina.valves.RemoteIpValve;
import org.apache.coyote.http11.AbstractHttp11Protocol;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.MutablePropertyValues;
import org.springframework.boot.bind.RelaxedDataBinder; import org.springframework.boot.bind.RelaxedDataBinder;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer; import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.core.IsInstanceOf.instanceOf; import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@ -106,18 +101,6 @@ public class ServerPropertiesTests {
.getInternalProxies()); .getInternalProxies());
} }
@Test
public void testCompressionBinding() throws Exception {
Map<String, String> map = new HashMap<String, String>();
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 @Test
public void testCustomizeTomcat() throws Exception { public void testCustomizeTomcat() throws Exception {
ConfigurableEmbeddedServletContainer factory = mock(ConfigurableEmbeddedServletContainer.class); ConfigurableEmbeddedServletContainer factory = mock(ConfigurableEmbeddedServletContainer.class);
@ -243,72 +226,9 @@ public class ServerPropertiesTests {
assertEquals("192.168.0.1", remoteIpValve.getInternalProxies()); assertEquals("192.168.0.1", remoteIpValve.getInternalProxies());
} }
@Test
public void customTomcatCompression() throws Exception {
assertThat("on", is(equalTo(configureCompression("on"))));
}
@Test
public void disableTomcatCompressionWithYaml() throws Exception {
// YAML interprets "off" as false, check that it's mapped back to off
assertThat("off", is(equalTo(configureCompression("faLSe"))));
}
@Test
public void enableTomcatCompressionWithYaml() throws Exception {
// YAML interprets "on" as true, check that it's mapped back to on
assertThat("on", is(equalTo(configureCompression("trUE"))));
}
@Test
public void customTomcatCompressableMimeTypes() throws Exception {
Map<String, String> map = new HashMap<String, String>();
map.put("server.port", "0");
map.put("server.tomcat.compressableMimeTypes", "application/foo");
bindProperties(map);
TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
this.properties.customize(factory);
TomcatEmbeddedServletContainer container = (TomcatEmbeddedServletContainer) factory
.getEmbeddedServletContainer();
try {
AbstractHttp11Protocol<?> protocol = (AbstractHttp11Protocol<?>) container
.getTomcat().getConnector().getProtocolHandler();
assertEquals("application/foo", protocol.getCompressableMimeTypes());
}
finally {
container.stop();
}
}
private void bindProperties(Map<String, String> map) { private void bindProperties(Map<String, String> map) {
new RelaxedDataBinder(this.properties, "server").bind(new MutablePropertyValues( new RelaxedDataBinder(this.properties, "server").bind(new MutablePropertyValues(
map)); map));
} }
private String configureCompression(String compression) {
Map<String, String> map = new HashMap<String, String>();
map.put("server.port", "0");
// YAML interprets "on" as true
map.put("server.tomcat.compression", compression);
bindProperties(map);
TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
this.properties.customize(factory);
TomcatEmbeddedServletContainer container = (TomcatEmbeddedServletContainer) factory
.getEmbeddedServletContainer();
try {
AbstractHttp11Protocol<?> protocol = (AbstractHttp11Protocol<?>) container
.getTomcat().getConnector().getProtocolHandler();
return protocol.getCompression();
}
finally {
container.stop();
}
}
} }

View File

@ -66,6 +66,9 @@ content into your application; rather pick only the properties that you need.
server.port=8080 server.port=8080
server.address= # bind to a specific NIC server.address= # bind to a specific NIC
server.session-timeout= # session timeout in seconds server.session-timeout= # session timeout in seconds
server.compression.enabled=false # if response compression is enabled
server.compression.mime-types=text/html,text/xml,text/plain,text/css # comma-separated list of MIME types that should be compressed
server.compression.min-response-size=2048 # minimum response size that is required for compression to be performed
server.context-parameters.*= # Servlet context init parameters, e.g. server.context-parameters.a=alpha server.context-parameters.*= # Servlet context init parameters, e.g. server.context-parameters.a=alpha
server.context-path= # the context path, defaults to '/' server.context-path= # the context path, defaults to '/'
server.jsp-servlet.class-name=org.apache.jasper.servlet.JspServlet # The class name of the JSP servlet server.jsp-servlet.class-name=org.apache.jasper.servlet.JspServlet # The class name of the JSP servlet
@ -89,8 +92,6 @@ content into your application; rather pick only the properties that you need.
server.ssl.trust-store-type= server.ssl.trust-store-type=
server.tomcat.access-log-pattern= # log pattern of the access log server.tomcat.access-log-pattern= # log pattern of the access log
server.tomcat.access-log-enabled=false # is access logging enabled server.tomcat.access-log-enabled=false # is access logging enabled
server.tomcat.compression=off # is compression enabled (off, on, or an integer content length limit)
server.tomcat.compressable-mime-types=text/html,text/xml,text/plain # comma-separated list of mime types that Tomcat will compress
server.tomcat.internal-proxies=10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|\\ server.tomcat.internal-proxies=10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|\\
192\\.168\\.\\d{1,3}\\.\\d{1,3}|\\ 192\\.168\\.\\d{1,3}\\.\\d{1,3}|\\
169\\.254\\.\\d{1,3}\\.\\d{1,3}|\\ 169\\.254\\.\\d{1,3}\\.\\d{1,3}|\\
@ -156,21 +157,6 @@ content into your application; rather pick only the properties that you need.
# HTTP message conversion # HTTP message conversion
spring.http.converters.preferred-json-mapper= # the preferred JSON mapper to use for HTTP message conversion. Set to "gson" to force the use of Gson when both it and Jackson are on the classpath. spring.http.converters.preferred-json-mapper= # the preferred JSON mapper to use for HTTP message conversion. Set to "gson" to force the use of Gson when both it and Jackson are on the classpath.
# HTTP response compression ({sc-spring-boot-autoconfigure}/web/GzipFilterProperties.{sc-ext}[GzipFilterProperties])
spring.http.gzip.buffer-size= # size of the output buffer in bytes
spring.http.gzip.deflate-compression-level= # the level used for deflate compression (0-9)
spring.http.gzip.deflate-no-wrap= # noWrap setting for deflate compression (true or false)
spring.http.gzip.enabled=true # enable gzip filter support
spring.http.gzip.excluded-agents= # comma-separated list of user agents to exclude from compression
spring.http.gzip.exclude-agent-patterns= # comma-separated list of regular expression patterns to control user agents excluded from compression
spring.http.gzip.exclude-paths= # comma-separated list of paths to exclude from compression
spring.http.gzip.exclude-path-patterns= # comma-separated list of regular expression patterns to control the paths that are excluded from compression
spring.http.gzip.methods= # comma-separated list of HTTP methods for which compression is enabled
spring.http.gzip.mime-types= # comma-separated list of MIME types which should be compressed
spring.http.gzip.excluded-mime-types= # comma-separated list of MIME types to exclude from compression
spring.http.gzip.min-gzip-size= # minimum content length required for compression to occur
spring.http.gzip.vary= # Vary header to be sent on responses that may be compressed
# JACKSON ({sc-spring-boot-autoconfigure}/jackson/JacksonProperties.{sc-ext}[JacksonProperties]) # JACKSON ({sc-spring-boot-autoconfigure}/jackson/JacksonProperties.{sc-ext}[JacksonProperties])
spring.jackson.date-format= # Date format string (e.g. yyyy-MM-dd HH:mm:ss), or a fully-qualified date format class name (e.g. com.fasterxml.jackson.databind.util.ISO8601DateFormat) spring.jackson.date-format= # Date format string (e.g. yyyy-MM-dd HH:mm:ss), or a fully-qualified date format class name (e.g. com.fasterxml.jackson.databind.util.ISO8601DateFormat)
spring.jackson.property-naming-strategy= # One of the constants on Jackson's PropertyNamingStrategy (e.g. CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES) or the fully-qualified class name of a PropertyNamingStrategy subclass spring.jackson.property-naming-strategy= # One of the constants on Jackson's PropertyNamingStrategy (e.g. CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES) or the fully-qualified class name of a PropertyNamingStrategy subclass

View File

@ -834,53 +834,27 @@ not required.
[[how-to-enable-http-response-compression]] [[how-to-enable-http-response-compression]]
=== Enable HTTP response compression === Enable HTTP response compression
Spring Boot provides two mechanisms for enabling compression of HTTP compression; one HTTP response compression is supported by Jetty, Tomcat, and Undertow. It can be enabled
that is Tomcat-specific and another that uses a filter and works with Jetty, Tomcat, via `application.properties`:
and Undertow.
[[how-to-enable-http-response-compression-tomcat]]
==== Enable Tomcat's HTTP response compression
Tomcat provides built-in support for HTTP response compression. It is disabled by
default, but can easily be enabled via `application.properties`:
[source,properties,indent=0,subs="verbatim,quotes,attributes"] [source,properties,indent=0,subs="verbatim,quotes,attributes"]
---- ----
server.tomcat.compression=on server.compression.enabled=true
---- ----
When set to `on` Tomcat will compress responses with a length that is at least 2048 By default, responses must be at least 2048 bytes in length for compression to be
bytes. This limit can be configured by specifying an integer value rather than `on`, performed. This can be configured using the `server.compression.min-response-size`
e.g.: property.
[source,properties,indent=0,subs="verbatim,quotes,attributes"] By default, responses will only be compressed if their content type is one of the
---- following:
server.tomcat.compression=4096
----
By default Tomcat will only compress responses with certain MIME types - `text/html`
(`text/html`, `text/xml`, and `text/plain`). You can customize this using the - `text/xml`
`server.tomcat.compressableMimeTypes` property, e.g.: - `text/plain`
- `text/css`
[source,properties,indent=0,subs="verbatim,quotes,attributes"] This can be configured using the `server.compression.mime-types` property.
----
server.tomcat.compressableMimeTypes=application/json,application/xml
----
[[how-to-enable-http-compression-gzip-filter]]
==== Enable HTTP response compression using GzipFilter
If you're using Jetty or Undertow, or you want more sophisticated control over
HTTP response compression, Spring Boot provides auto-configuration for Jetty's
`GzipFilter`. While this filter is part of Jetty, it's compatible with Tomcat
and Undertow as well. To enable the filter, simply add a dependency on
`org.eclipse.jetty:jetty-servlets` to your application.
`GzipFilter` can be configured using the `spring.http.gzip.*` properties. See
{sc-spring-boot-autoconfigure}/web/GzipFilterProperties.{sc-ext}[`GzipFilterProperties`]
for more details.

View File

@ -51,9 +51,10 @@ public class WarPackagingTests {
"tomcat-embed-websocket-")); "tomcat-embed-websocket-"));
private static final Set<String> JETTY_EXPECTED_IN_WEB_INF_LIB_PROVIDED = new HashSet<String>( private static final Set<String> JETTY_EXPECTED_IN_WEB_INF_LIB_PROVIDED = new HashSet<String>(
Arrays.asList("spring-boot-starter-jetty-", "jetty-util-", "javax.servlet-", Arrays.asList("spring-boot-starter-jetty-", "jetty-continuation",
"jetty-io-", "jetty-http-", "jetty-server-", "jetty-security-", "jetty-util-", "javax.servlet-", "jetty-io-", "jetty-http-",
"jetty-servlet-", "jetty-webapp-", "websocket-api", "jetty-server-", "jetty-security-", "jetty-servlet-",
"jetty-servlets", "jetty-webapp-", "websocket-api",
"javax.annotation-api", "jetty-plus", "javax-websocket-server-impl-", "javax.annotation-api", "jetty-plus", "javax-websocket-server-impl-",
"asm-", "javax.websocket-api-", "asm-tree-", "asm-commons-", "asm-", "javax.websocket-api-", "asm-tree-", "asm-commons-",
"websocket-common-", "jetty-annotations-", "websocket-common-", "jetty-annotations-",

View File

@ -0,0 +1,2 @@
server.compression.enabled: true
server.compression.min-response-size: 1

View File

@ -16,17 +16,26 @@
package sample.jetty; package sample.jetty;
import java.io.ByteArrayInputStream;
import java.nio.charset.Charset;
import java.util.zip.GZIPInputStream;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.IntegrationTest; import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.boot.test.TestRestTemplate; import org.springframework.boot.test.TestRestTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.util.StreamUtils;
import org.springframework.web.client.RestTemplate;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@ -34,6 +43,7 @@ import static org.junit.Assert.assertEquals;
* Basic integration tests for demo application. * Basic integration tests for demo application.
* *
* @author Dave Syer * @author Dave Syer
* @author Andy Wilkinson
*/ */
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SampleJettyApplication.class) @SpringApplicationConfiguration(classes = SampleJettyApplication.class)
@ -53,4 +63,28 @@ public class SampleJettyApplicationTests {
assertEquals("Hello World", entity.getBody()); assertEquals("Hello World", entity.getBody());
} }
@Test
public void testCompression() throws Exception {
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("Accept-Encoding", "gzip");
HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders);
RestTemplate restTemplate = new TestRestTemplate();
ResponseEntity<byte[]> entity = restTemplate.exchange("http://localhost:"
+ this.port, HttpMethod.GET, requestEntity, byte[].class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
GZIPInputStream inflater = new GZIPInputStream(new ByteArrayInputStream(
entity.getBody()));
try {
assertEquals("Hello World",
StreamUtils.copyToString(inflater, Charset.forName("UTF-8")));
}
finally {
inflater.close();
}
}
} }

View File

@ -0,0 +1,2 @@
server.compression.enabled: true
server.compression.min-response-size: 1

View File

@ -16,25 +16,34 @@
package sample.jetty8; package sample.jetty8;
import java.io.ByteArrayInputStream;
import java.nio.charset.Charset;
import java.util.zip.GZIPInputStream;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.IntegrationTest; import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.boot.test.TestRestTemplate; import org.springframework.boot.test.TestRestTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.util.StreamUtils;
import org.springframework.web.client.RestTemplate;
import sample.jetty8.SampleJetty8Application;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
/** /**
* Basic integration tests for demo application. * Basic integration tests for demo application.
* *
* @author Dave Syer * @author Dave Syer
* @author Andy Wilkinson
*/ */
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SampleJetty8Application.class) @SpringApplicationConfiguration(classes = SampleJetty8Application.class)
@ -54,4 +63,28 @@ public class SampleJetty8ApplicationTests {
assertEquals("Hello World", entity.getBody()); assertEquals("Hello World", entity.getBody());
} }
@Test
public void testCompression() throws Exception {
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("Accept-Encoding", "gzip");
HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders);
RestTemplate restTemplate = new TestRestTemplate();
ResponseEntity<byte[]> entity = restTemplate.exchange("http://localhost:"
+ this.port, HttpMethod.GET, requestEntity, byte[].class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
GZIPInputStream inflater = new GZIPInputStream(new ByteArrayInputStream(
entity.getBody()));
try {
assertEquals("Hello World",
StreamUtils.copyToString(inflater, Charset.forName("UTF-8")));
}
finally {
inflater.close();
}
}
} }

View File

@ -0,0 +1,2 @@
server.compression.enabled: true
server.compression.min-response-size: 1

View File

@ -16,16 +16,25 @@
package sample.tomcat; package sample.tomcat;
import java.io.ByteArrayInputStream;
import java.nio.charset.Charset;
import java.util.zip.GZIPInputStream;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.boot.test.TestRestTemplate; import org.springframework.boot.test.TestRestTemplate;
import org.springframework.boot.test.WebIntegrationTest; import org.springframework.boot.test.WebIntegrationTest;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.StreamUtils;
import org.springframework.web.client.RestTemplate;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@ -33,6 +42,7 @@ import static org.junit.Assert.assertEquals;
* Basic integration tests for demo application. * Basic integration tests for demo application.
* *
* @author Dave Syer * @author Dave Syer
* @author Andy Wilkinson
*/ */
@RunWith(SpringJUnit4ClassRunner.class) @RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SampleTomcatApplication.class) @SpringApplicationConfiguration(classes = SampleTomcatApplication.class)
@ -51,4 +61,28 @@ public class SampleTomcatApplicationTests {
assertEquals("Hello World", entity.getBody()); assertEquals("Hello World", entity.getBody());
} }
@Test
public void testCompression() throws Exception {
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("Accept-Encoding", "gzip");
HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders);
RestTemplate restTemplate = new TestRestTemplate();
ResponseEntity<byte[]> entity = restTemplate.exchange("http://localhost:"
+ this.port, HttpMethod.GET, requestEntity, byte[].class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
GZIPInputStream inflater = new GZIPInputStream(new ByteArrayInputStream(
entity.getBody()));
try {
assertEquals("Hello World",
StreamUtils.copyToString(inflater, Charset.forName("UTF-8")));
}
finally {
inflater.close();
}
}
} }

View File

@ -1,3 +1,5 @@
server.undertow.access-log-enabled=true server.undertow.access-log-enabled=true
server.undertow.access-log-dir=target/logs server.undertow.access-log-dir=target/logs
server.undertow.access-log-pattern=combined server.undertow.access-log-pattern=combined
server.compression.enabled=true
server.compression.min-response-size=1

View File

@ -16,17 +16,26 @@
package sample.undertow; package sample.undertow;
import java.io.ByteArrayInputStream;
import java.nio.charset.Charset;
import java.util.zip.GZIPInputStream;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.IntegrationTest; import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.boot.test.TestRestTemplate; import org.springframework.boot.test.TestRestTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.util.StreamUtils;
import org.springframework.web.client.RestTemplate;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@ -56,6 +65,30 @@ public class SampleUndertowApplicationTests {
assertOkResponse("/async", "async: Hello World"); assertOkResponse("/async", "async: Hello World");
} }
@Test
public void testCompression() throws Exception {
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("Accept-Encoding", "gzip");
HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders);
RestTemplate restTemplate = new TestRestTemplate();
ResponseEntity<byte[]> entity = restTemplate.exchange("http://localhost:"
+ this.port, HttpMethod.GET, requestEntity, byte[].class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
GZIPInputStream inflater = new GZIPInputStream(new ByteArrayInputStream(
entity.getBody()));
try {
assertEquals("Hello World",
StreamUtils.copyToString(inflater, Charset.forName("UTF-8")));
}
finally {
inflater.close();
}
}
private void assertOkResponse(String path, String body) { private void assertOkResponse(String path, String body) {
ResponseEntity<String> entity = new TestRestTemplate().getForEntity( ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
"http://localhost:" + this.port + path, String.class); "http://localhost:" + this.port + path, String.class);

View File

@ -18,6 +18,10 @@
<main.basedir>${basedir}/../..</main.basedir> <main.basedir>${basedir}/../..</main.basedir>
</properties> </properties>
<dependencies> <dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlets</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId> <artifactId>jetty-webapp</artifactId>

View File

@ -23,7 +23,6 @@ import java.util.Arrays;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.springframework.util.Assert; import org.springframework.util.Assert;
@ -36,6 +35,7 @@ import org.springframework.util.ClassUtils;
* @author Dave Syer * @author Dave Syer
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Ivan Sopov
* @see AbstractEmbeddedServletContainerFactory * @see AbstractEmbeddedServletContainerFactory
*/ */
public abstract class AbstractConfigurableEmbeddedServletContainer implements public abstract class AbstractConfigurableEmbeddedServletContainer implements
@ -68,7 +68,7 @@ public abstract class AbstractConfigurableEmbeddedServletContainer implements
private JspServlet jspServlet = new JspServlet(); private JspServlet jspServlet = new JspServlet();
private CompressionProperties compression; private Compression compression;
/** /**
* Create a new {@link AbstractConfigurableEmbeddedServletContainer} instance. * Create a new {@link AbstractConfigurableEmbeddedServletContainer} instance.
@ -281,11 +281,11 @@ public abstract class AbstractConfigurableEmbeddedServletContainer implements
return this.jspServlet; return this.jspServlet;
} }
public CompressionProperties getCompression() { public Compression getCompression() {
return this.compression; return this.compression;
} }
public void setCompression(CompressionProperties compression) { public void setCompression(Compression compression) {
this.compression = compression; this.compression = compression;
} }
@ -317,44 +317,4 @@ public abstract class AbstractConfigurableEmbeddedServletContainer implements
.getClassLoader()); .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<String> getMimeTypesList() {
List<String> mimeTypesList = new ArrayList<String>();
StringTokenizer tok = new StringTokenizer(mimeTypes, ",", false);
while (tok.hasMoreTokens()) {
mimeTypesList.add(tok.nextToken());
}
return mimeTypesList;
}
}
} }

View File

@ -0,0 +1,68 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.context.embedded;
/**
* Simple container-independent abstraction for compression configuration.
*
* @author Ivan Sopov
* @author Andy Wilkinson
* @since 1.3.0
*/
public class Compression {
/**
* If response compression is enabled.
*/
private boolean enabled = false;
/**
* Comma-separated list of MIME types that should be compressed.
*/
private String[] mimeTypes = new String[] { "text/html", "text/xml", "text/plain",
"text/css" };
/**
* Minimum response size that is required for compression to be performed
*/
private int minResponseSize = 2048;
public boolean getEnabled() {
return this.enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String[] getMimeTypes() {
return this.mimeTypes;
}
public void setMimeTypes(String[] mimeTypes) {
this.mimeTypes = mimeTypes;
}
public int getMinResponseSize() {
return this.minResponseSize;
}
public void setMinResponseSize(int minSize) {
this.minResponseSize = minSize;
}
}

View File

@ -170,4 +170,11 @@ public interface ConfigurableEmbeddedServletContainer {
*/ */
void setJspServlet(JspServlet jspServlet); void setJspServlet(JspServlet jspServlet);
/**
* Sets the compression configuration that will be applied to the container's default
* connector.
* @param compression the compression configuration
*/
void setCompression(Compression compression);
} }

View File

@ -18,13 +18,14 @@ package org.springframework.boot.context.embedded.jetty;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Method;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.http.MimeTypes;
@ -42,6 +43,7 @@ import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.servlet.ErrorPageErrorHandler; import org.eclipse.jetty.servlet.ErrorPageErrorHandler;
import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.servlet.ServletMapping; import org.eclipse.jetty.servlet.ServletMapping;
import org.eclipse.jetty.servlets.gzip.GzipHandler;
import org.eclipse.jetty.util.resource.JarResource; import org.eclipse.jetty.util.resource.JarResource;
import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.ssl.SslContextFactory;
@ -49,6 +51,7 @@ import org.eclipse.jetty.webapp.AbstractConfiguration;
import org.eclipse.jetty.webapp.Configuration; import org.eclipse.jetty.webapp.Configuration;
import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.webapp.WebAppContext;
import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.Compression;
import org.springframework.boot.context.embedded.EmbeddedServletContainer; import org.springframework.boot.context.embedded.EmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerException; import org.springframework.boot.context.embedded.EmbeddedServletContainerException;
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
@ -84,6 +87,12 @@ import org.springframework.util.StringUtils;
public class JettyEmbeddedServletContainerFactory extends public class JettyEmbeddedServletContainerFactory extends
AbstractEmbeddedServletContainerFactory implements ResourceLoaderAware { AbstractEmbeddedServletContainerFactory implements ResourceLoaderAware {
private static final String GZIP_HANDLER_JETTY_9_2 = "org.eclipse.jetty.servlets.gzip.GzipHandler";
private static final String GZIP_HANDLER_JETTY_8 = "org.eclipse.jetty.server.handler.GzipHandler";
private static final String GZIP_HANDLER_JETTY_9_3 = "org.eclipse.jetty.server.handler.gzip.GzipHandler";
private List<Configuration> configurations = new ArrayList<Configuration>(); private List<Configuration> configurations = new ArrayList<Configuration>();
private List<JettyServerCustomizer> jettyServerCustomizers = new ArrayList<JettyServerCustomizer>(); private List<JettyServerCustomizer> jettyServerCustomizers = new ArrayList<JettyServerCustomizer>();
@ -116,12 +125,6 @@ public class JettyEmbeddedServletContainerFactory extends
super(contextPath, port); 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 @Override
public EmbeddedServletContainer getEmbeddedServletContainer( public EmbeddedServletContainer getEmbeddedServletContainer(
ServletContextInitializer... initializers) { ServletContextInitializer... initializers) {
@ -129,8 +132,10 @@ public class JettyEmbeddedServletContainerFactory extends
int port = (getPort() >= 0 ? getPort() : 0); int port = (getPort() >= 0 ? getPort() : 0);
Server server = new Server(new InetSocketAddress(getAddress(), port)); Server server = new Server(new InetSocketAddress(getAddress(), port));
configureWebAppContext(context, initializers); configureWebAppContext(context, initializers);
if (getCompression() != null && getCompression().isEnabled()) { if (getCompression() != null && getCompression().getEnabled()) {
setupGzipHandler(context, server); HandlerWrapper gzipHandler = createGzipHandler();
gzipHandler.setHandler(context);
server.setHandler(gzipHandler);
} }
else { else {
server.setHandler(context); server.setHandler(context);
@ -149,50 +154,19 @@ public class JettyEmbeddedServletContainerFactory extends
return getJettyEmbeddedServletContainer(server); return getJettyEmbeddedServletContainer(server);
} }
private void setupGzipHandler(JettyEmbeddedWebAppContext context, Server server) private HandlerWrapper createGzipHandler() {
throws LinkageError { if (ClassUtils.isPresent(GZIP_HANDLER_JETTY_9_2, getClass().getClassLoader())) {
boolean done = false; return new Jetty92GzipHandlerFactory().createGzipHandler(getCompression());
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<String> 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) { else if (ClassUtils.isPresent(GZIP_HANDLER_JETTY_8, getClass().getClassLoader())) {
throw new IllegalStateException("Jetty GzipHandler is not in classpath"); return new Jetty8GzipHandlerFactory().createGzipHandler(getCompression());
} }
else if (ClassUtils
.isPresent(GZIP_HANDLER_JETTY_9_3, getClass().getClassLoader())) {
return new Jetty93GzipHandlerFactory().createGzipHandler(getCompression());
}
throw new IllegalStateException(
"Compression is enabled, but GzipHandler is not on the classpath");
} }
private SslServerConnectorFactory getSslServerConnectorFactory() { private SslServerConnectorFactory getSslServerConnectorFactory() {
@ -594,4 +568,73 @@ public class JettyEmbeddedServletContainerFactory extends
} }
private interface GzipHandlerFactory {
HandlerWrapper createGzipHandler(Compression compression);
}
private static class Jetty8GzipHandlerFactory implements GzipHandlerFactory {
@Override
public HandlerWrapper createGzipHandler(Compression compression) {
try {
Class<?> gzipHandlerClass = ClassUtils.forName(GZIP_HANDLER_JETTY_8,
getClass().getClassLoader());
HandlerWrapper gzipHandler = (HandlerWrapper) gzipHandlerClass
.newInstance();
ReflectionUtils.findMethod(gzipHandlerClass, "setMinGzipSize", int.class)
.invoke(gzipHandler, compression.getMinResponseSize());
ReflectionUtils.findMethod(gzipHandlerClass, "setMimeTypes", Set.class)
.invoke(gzipHandler,
new HashSet<String>(Arrays.asList(compression
.getMimeTypes())));
return gzipHandler;
}
catch (Exception ex) {
throw new RuntimeException("Failed to configure Jetty 8 gzip handler", ex);
}
}
}
private static class Jetty92GzipHandlerFactory implements GzipHandlerFactory {
@Override
public HandlerWrapper createGzipHandler(Compression compression) {
GzipHandler gzipHandler = new GzipHandler();
gzipHandler.setMinGzipSize(compression.getMinResponseSize());
gzipHandler.setMimeTypes(new HashSet<String>(Arrays.asList(compression
.getMimeTypes())));
return gzipHandler;
}
}
private static class Jetty93GzipHandlerFactory implements GzipHandlerFactory {
@Override
public HandlerWrapper createGzipHandler(Compression compression) {
try {
Class<?> gzipHandlerClass = ClassUtils.forName(GZIP_HANDLER_JETTY_9_3,
getClass().getClassLoader());
HandlerWrapper gzipHandler = (HandlerWrapper) gzipHandlerClass
.newInstance();
ReflectionUtils.findMethod(gzipHandlerClass, "setMinGzipSize", int.class)
.invoke(gzipHandler, compression.getMinResponseSize());
ReflectionUtils.findMethod(gzipHandlerClass, "setIncludedMimeTypes",
String[].class).invoke(gzipHandler,
new Object[] { compression.getMimeTypes() });
return gzipHandler;
}
catch (Exception ex) {
throw new RuntimeException("Failed to configure Jetty 9.3 gzip handler",
ex);
}
}
}
} }

View File

@ -257,14 +257,15 @@ public class TomcatEmbeddedServletContainerFactory extends
connector.setSecure(true); connector.setSecure(true);
} }
if (getCompression() != null && getCompression().isEnabled()) { if (getCompression() != null && getCompression().getEnabled()) {
ProtocolHandler handler = connector.getProtocolHandler(); ProtocolHandler handler = connector.getProtocolHandler();
if (handler instanceof AbstractHttp11Protocol) { if (handler instanceof AbstractHttp11Protocol) {
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
AbstractHttp11Protocol protocol = (AbstractHttp11Protocol) handler; AbstractHttp11Protocol protocol = (AbstractHttp11Protocol) handler;
protocol.setCompression("on"); protocol.setCompression("on");
protocol.setCompressionMinSize(getCompression().getMinSize()); protocol.setCompressionMinSize(getCompression().getMinResponseSize());
protocol.setCompressableMimeTypes(getCompression().getMimeTypes()); protocol.setCompressableMimeTypes(StringUtils
.arrayToCommaDelimitedString(getCompression().getMimeTypes()));
} }
} }

View File

@ -19,17 +19,14 @@ package org.springframework.boot.context.embedded.undertow;
import io.undertow.Handlers; import io.undertow.Handlers;
import io.undertow.Undertow; import io.undertow.Undertow;
import io.undertow.Undertow.Builder; 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.Predicate;
import io.undertow.predicate.Predicates; import io.undertow.predicate.Predicates;
import io.undertow.server.HttpHandler; import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.handlers.encoding.ContentEncodingRepository; import io.undertow.server.handlers.encoding.ContentEncodingRepository;
import io.undertow.server.handlers.encoding.EncodingHandler; import io.undertow.server.handlers.encoding.EncodingHandler;
import io.undertow.server.handlers.encoding.GzipEncodingProvider; import io.undertow.server.handlers.encoding.GzipEncodingProvider;
import io.undertow.servlet.api.DeploymentManager; import io.undertow.servlet.api.DeploymentManager;
import io.undertow.util.HttpString;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.net.ServerSocket; import java.net.ServerSocket;
@ -40,10 +37,12 @@ import javax.servlet.ServletException;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.boot.context.embedded.AbstractConfigurableEmbeddedServletContainer.CompressionProperties; import org.springframework.boot.context.embedded.Compression;
import org.springframework.boot.context.embedded.EmbeddedServletContainer; import org.springframework.boot.context.embedded.EmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerException; import org.springframework.boot.context.embedded.EmbeddedServletContainerException;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.util.MimeType;
import org.springframework.util.MimeTypeUtils;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
@ -70,15 +69,14 @@ public class UndertowEmbeddedServletContainer implements EmbeddedServletContaine
private final boolean autoStart; private final boolean autoStart;
private final CompressionProperties compression; private final Compression compression;
private Undertow undertow; private Undertow undertow;
private boolean started = false; private boolean started = false;
public UndertowEmbeddedServletContainer(Builder builder, DeploymentManager manager, public UndertowEmbeddedServletContainer(Builder builder, DeploymentManager manager,
String contextPath, int port, boolean autoStart, String contextPath, int port, boolean autoStart, Compression compression) {
CompressionProperties compression) {
this.builder = builder; this.builder = builder;
this.manager = manager; this.manager = manager;
this.contextPath = contextPath; this.contextPath = contextPath;
@ -113,33 +111,26 @@ public class UndertowEmbeddedServletContainer implements EmbeddedServletContaine
} }
private HttpHandler getContextHandler(HttpHandler servletHandler) { private HttpHandler getContextHandler(HttpHandler servletHandler) {
if (compression != null && compression.isEnabled()) { HttpHandler contextHandler = configurationCompressionIfNecessary(servletHandler);
ContentEncodingRepository encodingRepository = new ContentEncodingRepository();
List<String> 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)) { if (StringUtils.isEmpty(this.contextPath)) {
return contextHandler;
}
return Handlers.path().addPrefixPath(this.contextPath, contextHandler);
}
private HttpHandler configurationCompressionIfNecessary(HttpHandler servletHandler) {
if (this.compression == null || !this.compression.getEnabled()) {
return servletHandler; return servletHandler;
} }
return Handlers.path().addPrefixPath(this.contextPath, servletHandler); ContentEncodingRepository encodingRepository = new ContentEncodingRepository();
Predicate mimeAndSizePredicate = Predicates.and(Predicates
.maxContentSize(this.compression.getMinResponseSize()), Predicates
.or(new CompressibleMimeTypePredicate(this.compression.getMimeTypes())));
encodingRepository.addEncodingHandler("gzip", new GzipEncodingProvider(), 50,
mimeAndSizePredicate);
return new EncodingHandler(encodingRepository).setNext(servletHandler);
} }
private String getPortsDescription() { private String getPortsDescription() {
@ -250,4 +241,32 @@ public class UndertowEmbeddedServletContainer implements EmbeddedServletContaine
} }
private static class CompressibleMimeTypePredicate implements Predicate {
private final List<MimeType> mimeTypes;
CompressibleMimeTypePredicate(String[] mimeTypes) {
this.mimeTypes = new ArrayList<MimeType>(mimeTypes.length);
for (String mimeTypeString : mimeTypes) {
this.mimeTypes.add(MimeTypeUtils.parseMimeType(mimeTypeString));
}
}
@Override
public boolean resolve(HttpServerExchange value) {
String contentType = value.getResponseHeaders().getFirst(
HttpHeaders.CONTENT_TYPE);
if (contentType != null) {
for (MimeType mimeType : this.mimeTypes) {
if (mimeType.isCompatibleWith(MimeTypeUtils
.parseMimeType(contentType))) {
return true;
}
}
}
return false;
}
}
} }

View File

@ -51,7 +51,6 @@ import org.junit.Test;
import org.junit.rules.ExpectedException; import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder; import org.junit.rules.TemporaryFolder;
import org.mockito.InOrder; import org.mockito.InOrder;
import org.springframework.boot.context.embedded.AbstractConfigurableEmbeddedServletContainer.CompressionProperties;
import org.springframework.boot.context.embedded.Ssl.ClientAuth; import org.springframework.boot.context.embedded.Ssl.ClientAuth;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
@ -541,10 +540,11 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
@Test @Test
public void noCompressionForMimeType() throws Exception { public void noCompressionForMimeType() throws Exception {
assertFalse(internalTestCompression(10000, "text/html,text/xml,text/css")); assertFalse(internalTestCompression(10000, new String[] { "text/html",
"text/xml", "text/css" }));
} }
protected String setupFactoryForCompression(int contentSize, String mimeTypes) protected String setUpFactoryForCompression(int contentSize, String[] mimeTypes)
throws Exception { throws Exception {
char[] chars = new char[contentSize]; char[] chars = new char[contentSize];
Arrays.fill(chars, 'F'); Arrays.fill(chars, 'F');
@ -555,7 +555,7 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
FileCopyUtils.copy(testContent, FileCopyUtils.copy(testContent,
new FileWriter(this.temporaryFolder.newFile("test.txt"))); new FileWriter(this.temporaryFolder.newFile("test.txt")));
factory.setDocumentRoot(this.temporaryFolder.getRoot()); factory.setDocumentRoot(this.temporaryFolder.getRoot());
CompressionProperties compression = new CompressionProperties(); Compression compression = new Compression();
compression.setEnabled(true); compression.setEnabled(true);
if (mimeTypes != null) { if (mimeTypes != null) {
compression.setMimeTypes(mimeTypes); compression.setMimeTypes(mimeTypes);
@ -567,9 +567,9 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
return testContent; return testContent;
} }
private boolean internalTestCompression(int contentSize, String mimeTypes) private boolean internalTestCompression(int contentSize, String[] mimeTypes)
throws Exception { throws Exception {
String testContent = setupFactoryForCompression(contentSize, mimeTypes); String testContent = setUpFactoryForCompression(contentSize, mimeTypes);
class TestGzipInputStreamFactory implements InputStreamFactory { class TestGzipInputStreamFactory implements InputStreamFactory {
@ -577,11 +577,11 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
@Override @Override
public InputStream create(InputStream instream) throws IOException { public InputStream create(InputStream instream) throws IOException {
if (requested.get()) { if (this.requested.get()) {
throw new IllegalStateException( throw new IllegalStateException(
"On deflated InputStream already requested"); "On deflated InputStream already requested");
} }
requested.set(true); this.requested.set(true);
return new GZIPInputStream(instream); return new GZIPInputStream(instream);
} }

View File

@ -40,9 +40,9 @@ import org.junit.Test;
import org.mockito.InOrder; import org.mockito.InOrder;
import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactoryTests; import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactoryTests;
import org.springframework.boot.context.embedded.Compression;
import org.springframework.boot.context.embedded.ServletRegistrationBean; import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.boot.context.embedded.Ssl; import org.springframework.boot.context.embedded.Ssl;
import org.springframework.boot.context.embedded.AbstractConfigurableEmbeddedServletContainer.CompressionProperties;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
@ -185,8 +185,8 @@ public class JettyEmbeddedServletContainerFactoryTests extends
@Override @Override
@SuppressWarnings("serial") @SuppressWarnings("serial")
// work-around for Jetty issue - https://bugs.eclipse.org/bugs/show_bug.cgi?id=470646 // Workaround for Jetty issue - https://bugs.eclipse.org/bugs/show_bug.cgi?id=470646
protected String setupFactoryForCompression(final int contentSize, String mimeTypes) protected String setUpFactoryForCompression(final int contentSize, String[] mimeTypes)
throws Exception { throws Exception {
char[] chars = new char[contentSize]; char[] chars = new char[contentSize];
Arrays.fill(chars, 'F'); Arrays.fill(chars, 'F');
@ -194,7 +194,7 @@ public class JettyEmbeddedServletContainerFactoryTests extends
AbstractEmbeddedServletContainerFactory factory = getFactory(); AbstractEmbeddedServletContainerFactory factory = getFactory();
CompressionProperties compression = new CompressionProperties(); Compression compression = new Compression();
compression.setEnabled(true); compression.setEnabled(true);
if (mimeTypes != null) { if (mimeTypes != null) {
compression.setMimeTypes(mimeTypes); compression.setMimeTypes(mimeTypes);