Add auto-configuration for Jetty’s GzipFilter

Jetty’s GzipFilter is a container-agnostic Filter that can be used to
provide gzip and deflate encoding of HTTP responses. This commit adds
auto-configuration for GzipFilter that is enabled when
org.eclipse.jetty:jetty-servlets is on the classpath. The filter can
be configured using spring.http.gzip.*

See gh-2031
This commit is contained in:
Andy Wilkinson 2015-01-15 15:00:15 +00:00
parent ec17c00126
commit 69ea4beb8a
6 changed files with 405 additions and 5 deletions

View File

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

View File

@ -0,0 +1,51 @@
/*
* 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.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
public FilterRegistrationBean gzipFilter() {
FilterRegistrationBean registration = new FilterRegistrationBean(new GzipFilter());
registration.addUrlPatterns("/*");
registration.setInitParameters(this.properties.getAsInitParameters());
return registration;
}
}

View File

@ -0,0 +1,226 @@
/*
* 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
* @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;
/**
* The 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 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 excludedAgentPatterns;
/**
* Comma-separated list of paths to exclude from compression. Uses String.startsWith
* to determine a match against the request's path.
*/
private String excludedPaths;
/**
* Comma-separated list of regular expression patterns to control the paths that are
* excluded from compression.
*/
private String excludedPathPatterns;
/**
* 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 String getExcludedAgents() {
return this.excludedAgents;
}
public void setExcludedAgents(String excludedAgents) {
this.addInitParameter("excludedAgents", excludedAgents);
this.excludedAgents = excludedAgents;
}
public String getExcludedAgentPatterns() {
return this.excludedAgentPatterns;
}
public void setExcludedAgentPatterns(String excludedAgentPatterns) {
this.addInitParameter("excludedAgentPatterns", excludedAgentPatterns);
this.excludedAgentPatterns = excludedAgentPatterns;
}
public String getExcludedPaths() {
return this.excludedPaths;
}
public void setExcludedPaths(String excludedPaths) {
this.addInitParameter("excludedPaths", excludedPaths);
this.excludedPaths = excludedPaths;
}
public String getExcludedPathPatterns() {
return this.excludedPathPatterns;
}
public void setExcludedPathPatterns(String excludedPathPatterns) {
this.addInitParameter("excludedPathPatterns", excludedPathPatterns);
this.excludedPathPatterns = excludedPathPatterns;
}
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

@ -57,14 +57,15 @@ org.springframework.boot.autoconfigure.social.TwitterAutoConfiguration,\
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
org.springframework.boot.autoconfigure.velocity.VelocityAutoConfiguration,\
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.GzipFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.web.HttpEncodingAutoConfiguration,\
org.springframework.boot.autoconfigure.web.HttpMessageConvertersAutoConfiguration,\
org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration,\
org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration,\
org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration
# Template availability providers

View File

@ -0,0 +1,112 @@
/*
* 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.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.excludedAgents:excluded-agent-1,excluded-agent-2",
"spring.http.gzip.excludedAgentPatterns:agent-pattern-1,agent-pattern-2",
"spring.http.gzip.excludedPaths:/static/",
"spring.http.gzip.excludedPathPatterns:path-pattern",
"spring.http.gzip.vary:vary-header-value");
FilterRegistrationBean registrationBean = this.context.getBean("gzipFilter",
FilterRegistrationBean.class);
assertThat(registrationBean.getInitParameters().size(), equalTo(12));
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("excludedAgents"),
equalTo("excluded-agent-1,excluded-agent-2"));
assertThat(registrationBean.getInitParameters().get("excludedAgentPatterns"),
equalTo("agent-pattern-1,agent-pattern-2"));
assertThat(registrationBean.getInitParameters().get("excludedPaths"),
equalTo("/static/"));
assertThat(registrationBean.getInitParameters().get("excludedPathPatterns"),
equalTo("path-pattern"));
assertThat(registrationBean.getInitParameters().get("vary"),
equalTo("vary-header-value"));
}
private void createAndRefreshContext(String... pairs) {
this.context = new AnnotationConfigApplicationContext();
EnvironmentTestUtils.addEnvironment(this.context, pairs);
this.context.register(GzipFilterAutoConfiguration.class);
this.context.refresh();
}
}

View File

@ -983,6 +983,11 @@
<artifactId>jetty-security</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
@ -990,7 +995,7 @@
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<artifactId>jetty-servlets</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>