Commit d213cc05 authored by Phillip Webb's avatar Phillip Webb

Polish

parent cc3aea2b
...@@ -16,8 +16,6 @@ ...@@ -16,8 +16,6 @@
package org.springframework.boot.autoconfigure.web; package org.springframework.boot.autoconfigure.web;
import javax.annotation.PostConstruct;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
/** /**
...@@ -42,15 +40,6 @@ public class ResourceProperties { ...@@ -42,15 +40,6 @@ public class ResourceProperties {
private final Chain chain = new Chain(); private final Chain chain = new Chain();
@PostConstruct
public void setUpDefaults() {
if (this.chain.enabled == null && (this.chain.strategy.content.enabled
|| this.chain.strategy.fixed.enabled)) {
this.chain.enabled = true;
}
}
public Integer getCachePeriod() { public Integer getCachePeriod() {
return this.cachePeriod; return this.cachePeriod;
} }
...@@ -68,7 +57,7 @@ public class ResourceProperties { ...@@ -68,7 +57,7 @@ public class ResourceProperties {
} }
public Chain getChain() { public Chain getChain() {
return chain; return this.chain;
} }
/** /**
...@@ -77,8 +66,8 @@ public class ResourceProperties { ...@@ -77,8 +66,8 @@ public class ResourceProperties {
public static class Chain { public static class Chain {
/** /**
* Enable the Spring Resource Handling chain. Disabled by default unless * Enable the Spring Resource Handling chain. Disabled by default unless at least
* at least one strategy has been enabled. * one strategy has been enabled.
*/ */
private Boolean enabled; private Boolean enabled;
...@@ -95,7 +84,7 @@ public class ResourceProperties { ...@@ -95,7 +84,7 @@ public class ResourceProperties {
private final Strategy strategy = new Strategy(); private final Strategy strategy = new Strategy();
public Boolean getEnabled() { public Boolean getEnabled() {
return enabled; return this.enabled;
} }
public void setEnabled(boolean enabled) { public void setEnabled(boolean enabled) {
...@@ -103,7 +92,7 @@ public class ResourceProperties { ...@@ -103,7 +92,7 @@ public class ResourceProperties {
} }
public boolean isCache() { public boolean isCache() {
return cache; return this.cache;
} }
public void setCache(boolean cache) { public void setCache(boolean cache) {
...@@ -111,16 +100,17 @@ public class ResourceProperties { ...@@ -111,16 +100,17 @@ public class ResourceProperties {
} }
public Strategy getStrategy() { public Strategy getStrategy() {
return strategy; return this.strategy;
} }
public boolean isHtml5AppCache() { public boolean isHtml5AppCache() {
return html5AppCache; return this.html5AppCache;
} }
public void setHtml5AppCache(boolean html5AppCache) { public void setHtml5AppCache(boolean html5AppCache) {
this.html5AppCache = html5AppCache; this.html5AppCache = html5AppCache;
} }
} }
/** /**
...@@ -133,12 +123,13 @@ public class ResourceProperties { ...@@ -133,12 +123,13 @@ public class ResourceProperties {
private final Content content = new Content(); private final Content content = new Content();
public Fixed getFixed() { public Fixed getFixed() {
return fixed; return this.fixed;
} }
public Content getContent() { public Content getContent() {
return content; return this.content;
} }
} }
/** /**
...@@ -154,10 +145,10 @@ public class ResourceProperties { ...@@ -154,10 +145,10 @@ public class ResourceProperties {
/** /**
* Comma-separated list of patterns to apply to the Version Strategy. * Comma-separated list of patterns to apply to the Version Strategy.
*/ */
private String[] paths = new String[]{"/**"}; private String[] paths = new String[] { "/**" };
public boolean isEnabled() { public boolean isEnabled() {
return enabled; return this.enabled;
} }
public void setEnabled(boolean enabled) { public void setEnabled(boolean enabled) {
...@@ -165,12 +156,13 @@ public class ResourceProperties { ...@@ -165,12 +156,13 @@ public class ResourceProperties {
} }
public String[] getPaths() { public String[] getPaths() {
return paths; return this.paths;
} }
public void setPaths(String[] paths) { public void setPaths(String[] paths) {
this.paths = paths; this.paths = paths;
} }
} }
/** /**
...@@ -194,7 +186,7 @@ public class ResourceProperties { ...@@ -194,7 +186,7 @@ public class ResourceProperties {
private String version; private String version;
public boolean isEnabled() { public boolean isEnabled() {
return enabled; return this.enabled;
} }
public void setEnabled(boolean enabled) { public void setEnabled(boolean enabled) {
...@@ -202,7 +194,7 @@ public class ResourceProperties { ...@@ -202,7 +194,7 @@ public class ResourceProperties {
} }
public String[] getPaths() { public String[] getPaths() {
return paths; return this.paths;
} }
public void setPaths(String[] paths) { public void setPaths(String[] paths) {
...@@ -210,11 +202,13 @@ public class ResourceProperties { ...@@ -210,11 +202,13 @@ public class ResourceProperties {
} }
public String getVersion() { public String getVersion() {
return version; return this.version;
} }
public void setVersion(String version) { public void setVersion(String version) {
this.version = version; this.version = version;
} }
} }
} }
...@@ -38,6 +38,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; ...@@ -38,6 +38,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.web.ResourceProperties.Strategy;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.context.web.OrderedHiddenHttpMethodFilter; import org.springframework.boot.context.web.OrderedHiddenHttpMethodFilter;
import org.springframework.context.ResourceLoaderAware; import org.springframework.context.ResourceLoaderAware;
...@@ -55,7 +56,6 @@ import org.springframework.format.Formatter; ...@@ -55,7 +56,6 @@ import org.springframework.format.Formatter;
import org.springframework.format.FormatterRegistry; import org.springframework.format.FormatterRegistry;
import org.springframework.format.datetime.DateFormatter; import org.springframework.format.datetime.DateFormatter;
import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.validation.DefaultMessageCodesResolver; import org.springframework.validation.DefaultMessageCodesResolver;
import org.springframework.validation.MessageCodesResolver; import org.springframework.validation.MessageCodesResolver;
...@@ -81,6 +81,7 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl ...@@ -81,6 +81,7 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.resource.AppCacheManifestTransformer; import org.springframework.web.servlet.resource.AppCacheManifestTransformer;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler; import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
import org.springframework.web.servlet.resource.ResourceResolver;
import org.springframework.web.servlet.resource.VersionResourceResolver; import org.springframework.web.servlet.resource.VersionResourceResolver;
import org.springframework.web.servlet.view.BeanNameViewResolver; import org.springframework.web.servlet.view.BeanNameViewResolver;
import org.springframework.web.servlet.view.ContentNegotiatingViewResolver; import org.springframework.web.servlet.view.ContentNegotiatingViewResolver;
...@@ -260,42 +261,51 @@ public class WebMvcAutoConfiguration { ...@@ -260,42 +261,51 @@ public class WebMvcAutoConfiguration {
} }
Integer cachePeriod = this.resourceProperties.getCachePeriod(); Integer cachePeriod = this.resourceProperties.getCachePeriod();
if (!registry.hasMappingForPattern("/webjars/**")) { if (!registry.hasMappingForPattern("/webjars/**")) {
ResourceHandlerRegistration registration = registry.addResourceHandler("/webjars/**") registerResourceChain(registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/") .addResourceLocations("classpath:/META-INF/resources/webjars/")
.setCachePeriod(cachePeriod); .setCachePeriod(cachePeriod));
registerResourceChain(registration);
} }
if (!registry.hasMappingForPattern("/**")) { if (!registry.hasMappingForPattern("/**")) {
ResourceHandlerRegistration registration = registry.addResourceHandler("/**") registerResourceChain(registry.addResourceHandler("/**")
.addResourceLocations(RESOURCE_LOCATIONS) .addResourceLocations(RESOURCE_LOCATIONS)
.setCachePeriod(cachePeriod); .setCachePeriod(cachePeriod));
registerResourceChain(registration);
} }
} }
private void registerResourceChain(ResourceHandlerRegistration registration) { private void registerResourceChain(ResourceHandlerRegistration registration) {
ResourceProperties.Chain chainProperties = this.resourceProperties.getChain(); ResourceProperties.Chain properties = this.resourceProperties.getChain();
if (ObjectUtils.nullSafeEquals(chainProperties.getEnabled(), Boolean.TRUE)) { if (Boolean.TRUE.equals(properties.getEnabled())
ResourceChainRegistration chain = registration.resourceChain(chainProperties.isCache()); || properties.getStrategy().getFixed().isEnabled()
boolean hasFixedVersionConfigured = chainProperties.getStrategy().getFixed().isEnabled(); || properties.getStrategy().getContent().isEnabled()) {
boolean hasContentVersionConfigured = chainProperties.getStrategy().getContent().isEnabled(); configureResourceChain(properties,
if (hasFixedVersionConfigured || hasContentVersionConfigured) { registration.resourceChain(properties.isCache()));
VersionResourceResolver versionResourceResolver = new VersionResourceResolver(); }
if (hasFixedVersionConfigured) { }
versionResourceResolver.addFixedVersionStrategy(
chainProperties.getStrategy().getFixed().getVersion(), private void configureResourceChain(ResourceProperties.Chain properties,
chainProperties.getStrategy().getFixed().getPaths()); ResourceChainRegistration chain) {
} Strategy strategy = properties.getStrategy();
if (hasContentVersionConfigured) { if (strategy.getFixed().isEnabled() || strategy.getContent().isEnabled()) {
versionResourceResolver. chain.addResolver(getVersionResourceResolver(strategy));
addContentVersionStrategy(chainProperties.getStrategy().getContent().getPaths());
}
chain.addResolver(versionResourceResolver);
}
if (chainProperties.isHtml5AppCache()) {
chain.addTransformer(new AppCacheManifestTransformer());
}
} }
if (properties.isHtml5AppCache()) {
chain.addTransformer(new AppCacheManifestTransformer());
}
}
private ResourceResolver getVersionResourceResolver(
ResourceProperties.Strategy properties) {
VersionResourceResolver resolver = new VersionResourceResolver();
if (properties.getFixed().isEnabled()) {
String version = properties.getFixed().getVersion();
String[] paths = properties.getFixed().getPaths();
resolver.addFixedVersionStrategy(version, paths);
}
if (properties.getContent().isEnabled()) {
String[] paths = properties.getContent().getPaths();
resolver.addContentVersionStrategy(paths);
}
return resolver;
} }
@Override @Override
......
...@@ -28,6 +28,7 @@ import java.util.Map; ...@@ -28,6 +28,7 @@ import java.util.Map;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.hamcrest.Matcher;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.junit.After; import org.junit.After;
import org.junit.Rule; import org.junit.Rule;
...@@ -170,15 +171,16 @@ public class WebMvcAutoConfigurationTests { ...@@ -170,15 +171,16 @@ public class WebMvcAutoConfigurationTests {
@Test @Test
public void resourceHandlerChainEnabled() throws Exception { public void resourceHandlerChainEnabled() throws Exception {
load("spring.resources.chain.enabled:true"); load("spring.resources.chain.enabled:true");
assertThat(getResourceResolvers("/webjars/**").size(), equalTo(2)); assertThat(getResourceResolvers("/webjars/**").size(), equalTo(2));
assertThat(getResourceTransformers("/webjars/**").size(), equalTo(1)); assertThat(getResourceTransformers("/webjars/**").size(), equalTo(1));
assertThat(getResourceResolvers("/**").size(), equalTo(2)); assertThat(getResourceResolvers("/**").size(), equalTo(2));
assertThat(getResourceTransformers("/**").size(), equalTo(1)); assertThat(getResourceTransformers("/**").size(), equalTo(1));
assertThat(
assertThat(getResourceResolvers("/**"), contains(instanceOf(CachingResourceResolver.class), getResourceResolvers("/**"),
instanceOf(PathResourceResolver.class))); containsInstances(CachingResourceResolver.class,
assertThat(getResourceTransformers("/**"), contains(instanceOf(CachingResourceTransformer.class))); PathResourceResolver.class));
assertThat(getResourceTransformers("/**"),
contains(instanceOf(CachingResourceTransformer.class)));
} }
@Test @Test
...@@ -186,38 +188,44 @@ public class WebMvcAutoConfigurationTests { ...@@ -186,38 +188,44 @@ public class WebMvcAutoConfigurationTests {
load("spring.resources.chain.strategy.fixed.enabled:true", load("spring.resources.chain.strategy.fixed.enabled:true",
"spring.resources.chain.strategy.fixed.version:test", "spring.resources.chain.strategy.fixed.version:test",
"spring.resources.chain.strategy.fixed.paths:/**/*.js"); "spring.resources.chain.strategy.fixed.paths:/**/*.js");
assertThat(getResourceResolvers("/webjars/**").size(), equalTo(3)); assertThat(getResourceResolvers("/webjars/**").size(), equalTo(3));
assertThat(getResourceTransformers("/webjars/**").size(), equalTo(2)); assertThat(getResourceTransformers("/webjars/**").size(), equalTo(2));
assertThat(getResourceResolvers("/**").size(), equalTo(3)); assertThat(getResourceResolvers("/**").size(), equalTo(3));
assertThat(getResourceTransformers("/**").size(), equalTo(2)); assertThat(getResourceTransformers("/**").size(), equalTo(2));
assertThat(
assertThat(getResourceResolvers("/**"), contains(instanceOf(CachingResourceResolver.class), getResourceResolvers("/**"),
instanceOf(VersionResourceResolver.class), containsInstances(CachingResourceResolver.class,
instanceOf(PathResourceResolver.class))); VersionResourceResolver.class, PathResourceResolver.class));
assertThat(getResourceTransformers("/**"), contains(instanceOf(CachingResourceTransformer.class), assertThat(
instanceOf(CssLinkResourceTransformer.class))); getResourceTransformers("/**"),
VersionResourceResolver resolver = (VersionResourceResolver) getResourceResolvers("/**").get(1); containsInstances(CachingResourceTransformer.class,
assertThat(resolver.getStrategyMap().get("/**/*.js"), instanceOf(FixedVersionStrategy.class)); CssLinkResourceTransformer.class));
VersionResourceResolver resolver = (VersionResourceResolver) getResourceResolvers(
"/**").get(1);
assertThat(resolver.getStrategyMap().get("/**/*.js"),
instanceOf(FixedVersionStrategy.class));
} }
@Test @Test
public void resourceHandlerContentStrategyEnabled() throws Exception { public void resourceHandlerContentStrategyEnabled() throws Exception {
load("spring.resources.chain.strategy.content.enabled:true", load("spring.resources.chain.strategy.content.enabled:true",
"spring.resources.chain.strategy.content.paths:/**,/*.png"); "spring.resources.chain.strategy.content.paths:/**,/*.png");
assertThat(getResourceResolvers("/webjars/**").size(), equalTo(3)); assertThat(getResourceResolvers("/webjars/**").size(), equalTo(3));
assertThat(getResourceTransformers("/webjars/**").size(), equalTo(2)); assertThat(getResourceTransformers("/webjars/**").size(), equalTo(2));
assertThat(getResourceResolvers("/**").size(), equalTo(3)); assertThat(getResourceResolvers("/**").size(), equalTo(3));
assertThat(getResourceTransformers("/**").size(), equalTo(2)); assertThat(getResourceTransformers("/**").size(), equalTo(2));
assertThat(
assertThat(getResourceResolvers("/**"), contains(instanceOf(CachingResourceResolver.class), getResourceResolvers("/**"),
instanceOf(VersionResourceResolver.class), containsInstances(CachingResourceResolver.class,
instanceOf(PathResourceResolver.class))); VersionResourceResolver.class, PathResourceResolver.class));
assertThat(getResourceTransformers("/**"), contains(instanceOf(CachingResourceTransformer.class), assertThat(
instanceOf(CssLinkResourceTransformer.class))); getResourceTransformers("/**"),
VersionResourceResolver resolver = (VersionResourceResolver) getResourceResolvers("/**").get(1); containsInstances(CachingResourceTransformer.class,
assertThat(resolver.getStrategyMap().get("/*.png"), instanceOf(ContentVersionStrategy.class)); CssLinkResourceTransformer.class));
VersionResourceResolver resolver = (VersionResourceResolver) getResourceResolvers(
"/**").get(1);
assertThat(resolver.getStrategyMap().get("/*.png"),
instanceOf(ContentVersionStrategy.class));
} }
@Test @Test
...@@ -229,20 +237,24 @@ public class WebMvcAutoConfigurationTests { ...@@ -229,20 +237,24 @@ public class WebMvcAutoConfigurationTests {
"spring.resources.chain.strategy.fixed.version:test", "spring.resources.chain.strategy.fixed.version:test",
"spring.resources.chain.strategy.fixed.paths:/**/*.js", "spring.resources.chain.strategy.fixed.paths:/**/*.js",
"spring.resources.chain.html5AppCache:true"); "spring.resources.chain.html5AppCache:true");
assertThat(getResourceResolvers("/webjars/**").size(), equalTo(2)); assertThat(getResourceResolvers("/webjars/**").size(), equalTo(2));
assertThat(getResourceTransformers("/webjars/**").size(), equalTo(2)); assertThat(getResourceTransformers("/webjars/**").size(), equalTo(2));
assertThat(getResourceResolvers("/**").size(), equalTo(2)); assertThat(getResourceResolvers("/**").size(), equalTo(2));
assertThat(getResourceTransformers("/**").size(), equalTo(2)); assertThat(getResourceTransformers("/**").size(), equalTo(2));
assertThat(
assertThat(getResourceResolvers("/**"), contains( getResourceResolvers("/**"),
instanceOf(VersionResourceResolver.class), instanceOf(PathResourceResolver.class))); containsInstances(VersionResourceResolver.class,
assertThat(getResourceTransformers("/**"), contains(instanceOf(CssLinkResourceTransformer.class), PathResourceResolver.class));
instanceOf(AppCacheManifestTransformer.class))); assertThat(
getResourceTransformers("/**"),
VersionResourceResolver resolver = (VersionResourceResolver) getResourceResolvers("/**").get(0); containsInstances(CssLinkResourceTransformer.class,
assertThat(resolver.getStrategyMap().get("/*.png"), instanceOf(ContentVersionStrategy.class)); AppCacheManifestTransformer.class));
assertThat(resolver.getStrategyMap().get("/**/*.js"), instanceOf(FixedVersionStrategy.class)); VersionResourceResolver resolver = (VersionResourceResolver) getResourceResolvers(
"/**").get(0);
assertThat(resolver.getStrategyMap().get("/*.png"),
instanceOf(ContentVersionStrategy.class));
assertThat(resolver.getStrategyMap().get("/**/*.js"),
instanceOf(FixedVersionStrategy.class));
} }
@Test @Test
...@@ -315,14 +327,18 @@ public class WebMvcAutoConfigurationTests { ...@@ -315,14 +327,18 @@ public class WebMvcAutoConfigurationTests {
} }
protected List<ResourceResolver> getResourceResolvers(String mapping) { protected List<ResourceResolver> getResourceResolvers(String mapping) {
SimpleUrlHandlerMapping handler = (SimpleUrlHandlerMapping) this.context.getBean("resourceHandlerMapping"); SimpleUrlHandlerMapping handler = (SimpleUrlHandlerMapping) this.context
ResourceHttpRequestHandler resourceHandler = (ResourceHttpRequestHandler) handler.getHandlerMap().get(mapping); .getBean("resourceHandlerMapping");
ResourceHttpRequestHandler resourceHandler = (ResourceHttpRequestHandler) handler
.getHandlerMap().get(mapping);
return resourceHandler.getResourceResolvers(); return resourceHandler.getResourceResolvers();
} }
protected List<ResourceTransformer> getResourceTransformers(String mapping) { protected List<ResourceTransformer> getResourceTransformers(String mapping) {
SimpleUrlHandlerMapping handler = (SimpleUrlHandlerMapping) this.context.getBean("resourceHandlerMapping"); SimpleUrlHandlerMapping handler = (SimpleUrlHandlerMapping) this.context
ResourceHttpRequestHandler resourceHandler = (ResourceHttpRequestHandler) handler.getHandlerMap().get(mapping); .getBean("resourceHandlerMapping");
ResourceHttpRequestHandler resourceHandler = (ResourceHttpRequestHandler) handler
.getHandlerMap().get(mapping);
return resourceHandler.getResourceTransformers(); return resourceHandler.getResourceTransformers();
} }
...@@ -440,6 +456,15 @@ public class WebMvcAutoConfigurationTests { ...@@ -440,6 +456,15 @@ public class WebMvcAutoConfigurationTests {
this.context.refresh(); this.context.refresh();
} }
@SuppressWarnings({ "unchecked", "rawtypes" })
private <E> Matcher<E> containsInstances(Class<?>... types) {
Matcher[] instances = new Matcher[types.length];
for (int i = 0; i < instances.length; i++) {
instances[i] = instanceOf(types[i]);
}
return contains(instances);
}
private void load(String... environment) { private void load(String... environment) {
load(null, environment); load(null, environment);
} }
......
...@@ -157,7 +157,7 @@ For example, the following will disable _all_ endpoints except for `info`: ...@@ -157,7 +157,7 @@ For example, the following will disable _all_ endpoints except for `info`:
Health information can be used to check the status of your running application. It is Health information can be used to check the status of your running application. It is
often used by monitoring software to alert someone if a production system goes down. often used by monitoring software to alert someone if a production system goes down.
The default information exposed by the `health` endpoint depends on how it is accessed. The default information exposed by the `health` endpoint depends on how it is accessed.
For an unauthenticated connection in a secure application a simple '`status`' message is For an unauthenticated connection in a secure application a simple '`status`' message is
returned, and for an authenticated connection additional details are also displayed (see returned, and for an authenticated connection additional details are also displayed (see
<<production-ready-health-access-restrictions>> for HTTP details). <<production-ready-health-access-restrictions>> for HTTP details).
...@@ -507,7 +507,7 @@ If you don't want to expose endpoints over HTTP you can set the management port ...@@ -507,7 +507,7 @@ If you don't want to expose endpoints over HTTP you can set the management port
[[production-ready-health-access-restrictions]] [[production-ready-health-access-restrictions]]
=== HTTP health endpoint access restrictions === HTTP health endpoint access restrictions
The information exposed by the health endpoint varies depending on whether or not it's The information exposed by the health endpoint varies depending on whether or not it's
accessed anonymously, and whether or not the enclosing application is secure. accessed anonymously, and whether or not the enclosing application is secure.
By default, when accessed anonymously in a secure application, any details about the By default, when accessed anonymously in a secure application, any details about the
server's health are hidden and the endpoint will simply indicate whether or not the server server's health are hidden and the endpoint will simply indicate whether or not the server
is up or down. Furthermore, when accessed anonymously, the response is cached for a is up or down. Furthermore, when accessed anonymously, the response is cached for a
...@@ -515,24 +515,37 @@ configurable period to prevent the endpoint being used in a denial of service at ...@@ -515,24 +515,37 @@ configurable period to prevent the endpoint being used in a denial of service at
The `endpoints.health.time-to-live` property is used to configure the caching period in The `endpoints.health.time-to-live` property is used to configure the caching period in
milliseconds. It defaults to 1000, i.e. one second. milliseconds. It defaults to 1000, i.e. one second.
The above-described restrictions can be enhanced, thereby allowing only authenticated users full The above-described restrictions can be enhanced, thereby allowing only authenticated
access to the health endpoint in a secure application. To do so, set `endpoints.health.sensitive` to `true`. users full access to the health endpoint in a secure application. To do so, set
Here's a summary of behaviour (with default `sensitive` flag value "false" indicated in bold): `endpoints.health.sensitive` to `true`. Here's a summary of behavior (with default
`sensitive` flag value "`false`" indicated in bold):
|==== |====
|Secure | Sensitive | Unauthenticated behaviour | Authenticated behaviour |Secure |Sensitive |Unauthenticated |Authenticated
| false | **false** | Full content | Full content |false
|**false**
| false | true | Status only | Full content |Full content
|Full content
| true | **false** | Status only | Full content |false
|true
|Status only
|Full content
| true | true | No content | Full content |true
|**false**
|Status only
|Full content
|true
|true
|No content
|Full content
|==== |====
[[production-ready-jmx]] [[production-ready-jmx]]
== Monitoring and management over JMX == Monitoring and management over JMX
Java Management Extensions (JMX) provide a standard mechanism to monitor and manage Java Management Extensions (JMX) provide a standard mechanism to monitor and manage
......
...@@ -1205,7 +1205,8 @@ supported right now, but can be with custom template macros/helpers and the use ...@@ -1205,7 +1205,8 @@ supported right now, but can be with custom template macros/helpers and the use
When loading resources dynamically with, for example, a JavaScript module loader, renaming When loading resources dynamically with, for example, a JavaScript module loader, renaming
files is not an option. That's why other strategies are also supported and can be combined. files is not an option. That's why other strategies are also supported and can be combined.
A "fixed" strategy will add a static version string in the URL, without changing the file name: A "fixed" strategy will add a static version string in the URL, without changing the file
name:
[source,properties,indent=0,subs="verbatim,quotes,attributes"] [source,properties,indent=0,subs="verbatim,quotes,attributes"]
---- ----
...@@ -1231,6 +1232,7 @@ and in Spring Framework's {spring-reference}/#mvc-config-static-resources[refere ...@@ -1231,6 +1232,7 @@ and in Spring Framework's {spring-reference}/#mvc-config-static-resources[refere
==== ====
[[boot-features-spring-mvc-template-engines]] [[boot-features-spring-mvc-template-engines]]
==== Template engines ==== Template engines
As well as REST web services, you can also use Spring MVC to serve dynamic HTML content. As well as REST web services, you can also use Spring MVC to serve dynamic HTML content.
......
...@@ -68,14 +68,10 @@ public class SampleJetty8ApplicationTests { ...@@ -68,14 +68,10 @@ public class SampleJetty8ApplicationTests {
HttpHeaders requestHeaders = new HttpHeaders(); HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("Accept-Encoding", "gzip"); requestHeaders.set("Accept-Encoding", "gzip");
HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders); HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders);
RestTemplate restTemplate = new TestRestTemplate(); RestTemplate restTemplate = new TestRestTemplate();
ResponseEntity<byte[]> entity = restTemplate.exchange("http://localhost:" ResponseEntity<byte[]> entity = restTemplate.exchange("http://localhost:"
+ this.port, HttpMethod.GET, requestEntity, byte[].class); + this.port, HttpMethod.GET, requestEntity, byte[].class);
assertEquals(HttpStatus.OK, entity.getStatusCode()); assertEquals(HttpStatus.OK, entity.getStatusCode());
GZIPInputStream inflater = new GZIPInputStream(new ByteArrayInputStream( GZIPInputStream inflater = new GZIPInputStream(new ByteArrayInputStream(
entity.getBody())); entity.getBody()));
try { try {
......
...@@ -68,14 +68,10 @@ public class SampleJetty93ApplicationTests { ...@@ -68,14 +68,10 @@ public class SampleJetty93ApplicationTests {
HttpHeaders requestHeaders = new HttpHeaders(); HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("Accept-Encoding", "gzip"); requestHeaders.set("Accept-Encoding", "gzip");
HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders); HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders);
RestTemplate restTemplate = new TestRestTemplate(); RestTemplate restTemplate = new TestRestTemplate();
ResponseEntity<byte[]> entity = restTemplate.exchange("http://localhost:" ResponseEntity<byte[]> entity = restTemplate.exchange("http://localhost:"
+ this.port, HttpMethod.GET, requestEntity, byte[].class); + this.port, HttpMethod.GET, requestEntity, byte[].class);
assertEquals(HttpStatus.OK, entity.getStatusCode()); assertEquals(HttpStatus.OK, entity.getStatusCode());
GZIPInputStream inflater = new GZIPInputStream(new ByteArrayInputStream( GZIPInputStream inflater = new GZIPInputStream(new ByteArrayInputStream(
entity.getBody())); entity.getBody()));
try { try {
......
...@@ -66,14 +66,10 @@ public class SampleTomcatApplicationTests { ...@@ -66,14 +66,10 @@ public class SampleTomcatApplicationTests {
HttpHeaders requestHeaders = new HttpHeaders(); HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("Accept-Encoding", "gzip"); requestHeaders.set("Accept-Encoding", "gzip");
HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders); HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders);
RestTemplate restTemplate = new TestRestTemplate(); RestTemplate restTemplate = new TestRestTemplate();
ResponseEntity<byte[]> entity = restTemplate.exchange("http://localhost:" ResponseEntity<byte[]> entity = restTemplate.exchange("http://localhost:"
+ this.port, HttpMethod.GET, requestEntity, byte[].class); + this.port, HttpMethod.GET, requestEntity, byte[].class);
assertEquals(HttpStatus.OK, entity.getStatusCode()); assertEquals(HttpStatus.OK, entity.getStatusCode());
GZIPInputStream inflater = new GZIPInputStream(new ByteArrayInputStream( GZIPInputStream inflater = new GZIPInputStream(new ByteArrayInputStream(
entity.getBody())); entity.getBody()));
try { try {
......
...@@ -70,14 +70,10 @@ public class SampleUndertowApplicationTests { ...@@ -70,14 +70,10 @@ public class SampleUndertowApplicationTests {
HttpHeaders requestHeaders = new HttpHeaders(); HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.set("Accept-Encoding", "gzip"); requestHeaders.set("Accept-Encoding", "gzip");
HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders); HttpEntity<?> requestEntity = new HttpEntity<Object>(requestHeaders);
RestTemplate restTemplate = new TestRestTemplate(); RestTemplate restTemplate = new TestRestTemplate();
ResponseEntity<byte[]> entity = restTemplate.exchange("http://localhost:" ResponseEntity<byte[]> entity = restTemplate.exchange("http://localhost:"
+ this.port, HttpMethod.GET, requestEntity, byte[].class); + this.port, HttpMethod.GET, requestEntity, byte[].class);
assertEquals(HttpStatus.OK, entity.getStatusCode()); assertEquals(HttpStatus.OK, entity.getStatusCode());
GZIPInputStream inflater = new GZIPInputStream(new ByteArrayInputStream( GZIPInputStream inflater = new GZIPInputStream(new ByteArrayInputStream(
entity.getBody())); entity.getBody()));
try { try {
......
...@@ -285,6 +285,7 @@ public abstract class AbstractConfigurableEmbeddedServletContainer implements ...@@ -285,6 +285,7 @@ public abstract class AbstractConfigurableEmbeddedServletContainer implements
return this.compression; return this.compression;
} }
@Override
public void setCompression(Compression compression) { public void setCompression(Compression compression) {
this.compression = compression; this.compression = compression;
} }
......
...@@ -65,4 +65,4 @@ public class Compression { ...@@ -65,4 +65,4 @@ public class Compression {
this.minResponseSize = minSize; this.minResponseSize = minSize;
} }
} }
\ No newline at end of file
...@@ -155,14 +155,14 @@ public class JettyEmbeddedServletContainerFactory extends ...@@ -155,14 +155,14 @@ public class JettyEmbeddedServletContainerFactory extends
} }
private HandlerWrapper createGzipHandler() { private HandlerWrapper createGzipHandler() {
if (ClassUtils.isPresent(GZIP_HANDLER_JETTY_9_2, getClass().getClassLoader())) { ClassLoader classLoader = getClass().getClassLoader();
if (ClassUtils.isPresent(GZIP_HANDLER_JETTY_9_2, classLoader)) {
return new Jetty92GzipHandlerFactory().createGzipHandler(getCompression()); return new Jetty92GzipHandlerFactory().createGzipHandler(getCompression());
} }
else if (ClassUtils.isPresent(GZIP_HANDLER_JETTY_8, getClass().getClassLoader())) { if (ClassUtils.isPresent(GZIP_HANDLER_JETTY_8, getClass().getClassLoader())) {
return new Jetty8GzipHandlerFactory().createGzipHandler(getCompression()); return new Jetty8GzipHandlerFactory().createGzipHandler(getCompression());
} }
else if (ClassUtils if (ClassUtils.isPresent(GZIP_HANDLER_JETTY_9_3, getClass().getClassLoader())) {
.isPresent(GZIP_HANDLER_JETTY_9_3, getClass().getClassLoader())) {
return new Jetty93GzipHandlerFactory().createGzipHandler(getCompression()); return new Jetty93GzipHandlerFactory().createGzipHandler(getCompression());
} }
throw new IllegalStateException( throw new IllegalStateException(
...@@ -579,19 +579,17 @@ public class JettyEmbeddedServletContainerFactory extends ...@@ -579,19 +579,17 @@ public class JettyEmbeddedServletContainerFactory extends
@Override @Override
public HandlerWrapper createGzipHandler(Compression compression) { public HandlerWrapper createGzipHandler(Compression compression) {
try { try {
Class<?> gzipHandlerClass = ClassUtils.forName(GZIP_HANDLER_JETTY_8, Class<?> handlerClass = ClassUtils.forName(GZIP_HANDLER_JETTY_8,
getClass().getClassLoader()); getClass().getClassLoader());
HandlerWrapper gzipHandler = (HandlerWrapper) gzipHandlerClass HandlerWrapper handler = (HandlerWrapper) handlerClass.newInstance();
.newInstance(); ReflectionUtils.findMethod(handlerClass, "setMinGzipSize", int.class)
ReflectionUtils.findMethod(gzipHandlerClass, "setMinGzipSize", int.class) .invoke(handler, compression.getMinResponseSize());
.invoke(gzipHandler, compression.getMinResponseSize()); ReflectionUtils.findMethod(handlerClass, "setMimeTypes", Set.class)
ReflectionUtils.findMethod(gzipHandlerClass, "setMimeTypes", Set.class) .invoke(handler,
.invoke(gzipHandler,
new HashSet<String>(Arrays.asList(compression new HashSet<String>(Arrays.asList(compression
.getMimeTypes()))); .getMimeTypes())));
return gzipHandler; return handler;
} }
catch (Exception ex) { catch (Exception ex) {
throw new RuntimeException("Failed to configure Jetty 8 gzip handler", ex); throw new RuntimeException("Failed to configure Jetty 8 gzip handler", ex);
} }
...@@ -608,7 +606,6 @@ public class JettyEmbeddedServletContainerFactory extends ...@@ -608,7 +606,6 @@ public class JettyEmbeddedServletContainerFactory extends
gzipHandler.setMimeTypes(new HashSet<String>(Arrays.asList(compression gzipHandler.setMimeTypes(new HashSet<String>(Arrays.asList(compression
.getMimeTypes()))); .getMimeTypes())));
return gzipHandler; return gzipHandler;
} }
} }
...@@ -618,18 +615,16 @@ public class JettyEmbeddedServletContainerFactory extends ...@@ -618,18 +615,16 @@ public class JettyEmbeddedServletContainerFactory extends
@Override @Override
public HandlerWrapper createGzipHandler(Compression compression) { public HandlerWrapper createGzipHandler(Compression compression) {
try { try {
Class<?> gzipHandlerClass = ClassUtils.forName(GZIP_HANDLER_JETTY_9_3, Class<?> handlerClass = ClassUtils.forName(GZIP_HANDLER_JETTY_9_3,
getClass().getClassLoader()); getClass().getClassLoader());
HandlerWrapper gzipHandler = (HandlerWrapper) gzipHandlerClass HandlerWrapper handler = (HandlerWrapper) handlerClass.newInstance();
.newInstance(); ReflectionUtils.findMethod(handlerClass, "setMinGzipSize", int.class)
ReflectionUtils.findMethod(gzipHandlerClass, "setMinGzipSize", int.class) .invoke(handler, compression.getMinResponseSize());
.invoke(gzipHandler, compression.getMinResponseSize()); ReflectionUtils.findMethod(handlerClass, "setIncludedMimeTypes",
ReflectionUtils.findMethod(gzipHandlerClass, "setIncludedMimeTypes", String[].class).invoke(handler,
String[].class).invoke(gzipHandler,
new Object[] { compression.getMimeTypes() }); new Object[] { compression.getMimeTypes() });
return gzipHandler; return handler;
} }
catch (Exception ex) { catch (Exception ex) {
throw new RuntimeException("Failed to configure Jetty 9.3 gzip handler", throw new RuntimeException("Failed to configure Jetty 9.3 gzip handler",
ex); ex);
...@@ -637,4 +632,5 @@ public class JettyEmbeddedServletContainerFactory extends ...@@ -637,4 +632,5 @@ public class JettyEmbeddedServletContainerFactory extends
} }
} }
} }
...@@ -51,6 +51,7 @@ import org.apache.coyote.http11.AbstractHttp11JsseProtocol; ...@@ -51,6 +51,7 @@ import org.apache.coyote.http11.AbstractHttp11JsseProtocol;
import org.apache.coyote.http11.AbstractHttp11Protocol; import org.apache.coyote.http11.AbstractHttp11Protocol;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
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;
...@@ -233,10 +234,7 @@ public class TomcatEmbeddedServletContainerFactory extends ...@@ -233,10 +234,7 @@ public class TomcatEmbeddedServletContainerFactory extends
int port = (getPort() >= 0 ? getPort() : 0); int port = (getPort() >= 0 ? getPort() : 0);
connector.setPort(port); connector.setPort(port);
if (connector.getProtocolHandler() instanceof AbstractProtocol) { if (connector.getProtocolHandler() instanceof AbstractProtocol) {
if (getAddress() != null) { customizeProtocol((AbstractProtocol<?>) connector.getProtocolHandler());
((AbstractProtocol<?>) connector.getProtocolHandler())
.setAddress(getAddress());
}
} }
if (getUriEncoding() != null) { if (getUriEncoding() != null) {
connector.setURIEncoding(getUriEncoding()); connector.setURIEncoding(getUriEncoding());
...@@ -247,33 +245,44 @@ public class TomcatEmbeddedServletContainerFactory extends ...@@ -247,33 +245,44 @@ public class TomcatEmbeddedServletContainerFactory extends
connector.setProperty("bindOnInit", "false"); connector.setProperty("bindOnInit", "false");
if (getSsl() != null && getSsl().isEnabled()) { if (getSsl() != null && getSsl().isEnabled()) {
Assert.state( customizeSsl(connector);
connector.getProtocolHandler() instanceof AbstractHttp11JsseProtocol,
"To use SSL, the connector's protocol handler must be an "
+ "AbstractHttp11JsseProtocol subclass");
configureSsl((AbstractHttp11JsseProtocol<?>) connector.getProtocolHandler(),
getSsl());
connector.setScheme("https");
connector.setSecure(true);
} }
if (getCompression() != null && getCompression().getEnabled()) { if (getCompression() != null && getCompression().getEnabled()) {
ProtocolHandler handler = connector.getProtocolHandler(); customizeCompression(connector);
if (handler instanceof AbstractHttp11Protocol) {
@SuppressWarnings("rawtypes")
AbstractHttp11Protocol protocol = (AbstractHttp11Protocol) handler;
protocol.setCompression("on");
protocol.setCompressionMinSize(getCompression().getMinResponseSize());
protocol.setCompressableMimeTypes(StringUtils
.arrayToCommaDelimitedString(getCompression().getMimeTypes()));
}
} }
for (TomcatConnectorCustomizer customizer : this.tomcatConnectorCustomizers) { for (TomcatConnectorCustomizer customizer : this.tomcatConnectorCustomizers) {
customizer.customize(connector); customizer.customize(connector);
} }
} }
private void customizeProtocol(AbstractProtocol<?> protocol) {
if (getAddress() != null) {
protocol.setAddress(getAddress());
}
}
private void customizeSsl(Connector connector) {
ProtocolHandler handler = connector.getProtocolHandler();
Assert.state(handler instanceof AbstractHttp11JsseProtocol,
"To use SSL, the connector's protocol handler must be an "
+ "AbstractHttp11JsseProtocol subclass");
configureSsl((AbstractHttp11JsseProtocol<?>) handler, getSsl());
connector.setScheme("https");
connector.setSecure(true);
}
private void customizeCompression(Connector connector) {
ProtocolHandler handler = connector.getProtocolHandler();
if (handler instanceof AbstractHttp11Protocol) {
AbstractHttp11Protocol<?> protocol = (AbstractHttp11Protocol<?>) handler;
Compression compression = getCompression();
protocol.setCompression("on");
protocol.setCompressionMinSize(compression.getMinResponseSize());
protocol.setCompressableMimeTypes(StringUtils
.arrayToCommaDelimitedString(compression.getMimeTypes()));
}
}
/** /**
* Configure Tomcat's {@link AbstractHttp11JsseProtocol} for SSL. * Configure Tomcat's {@link AbstractHttp11JsseProtocol} for SSL.
* @param protocol the protocol * @param protocol the protocol
......
...@@ -123,11 +123,9 @@ public class UndertowEmbeddedServletContainer implements EmbeddedServletContaine ...@@ -123,11 +123,9 @@ public class UndertowEmbeddedServletContainer implements EmbeddedServletContaine
return servletHandler; return servletHandler;
} }
ContentEncodingRepository encodingRepository = new ContentEncodingRepository(); ContentEncodingRepository encodingRepository = new ContentEncodingRepository();
Predicate mimeAndSizePredicate = Predicates.and(Predicates Predicate mimeAndSizePredicate = Predicates.and(Predicates
.maxContentSize(this.compression.getMinResponseSize()), Predicates .maxContentSize(this.compression.getMinResponseSize()), Predicates
.or(new CompressibleMimeTypePredicate(this.compression.getMimeTypes()))); .or(new CompressibleMimeTypePredicate(this.compression.getMimeTypes())));
encodingRepository.addEncodingHandler("gzip", new GzipEncodingProvider(), 50, encodingRepository.addEncodingHandler("gzip", new GzipEncodingProvider(), 50,
mimeAndSizePredicate); mimeAndSizePredicate);
return new EncodingHandler(encodingRepository).setNext(servletHandler); return new EncodingHandler(encodingRepository).setNext(servletHandler);
......
...@@ -27,6 +27,7 @@ import java.nio.charset.Charset; ...@@ -27,6 +27,7 @@ import java.nio.charset.Charset;
import java.security.KeyStore; import java.security.KeyStore;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
...@@ -530,18 +531,31 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests { ...@@ -530,18 +531,31 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
@Test @Test
public void compression() throws Exception { public void compression() throws Exception {
assertTrue(internalTestCompression(10000, null)); assertTrue(doTestCompression(10000, null));
} }
@Test @Test
public void noCompressionForSmallResponse() throws Exception { public void noCompressionForSmallResponse() throws Exception {
assertFalse(internalTestCompression(100, null)); assertFalse(doTestCompression(100, null));
} }
@Test @Test
public void noCompressionForMimeType() throws Exception { public void noCompressionForMimeType() throws Exception {
assertFalse(internalTestCompression(10000, new String[] { "text/html", String[] mimeTypes = new String[] { "text/html", "text/xml", "text/css" };
"text/xml", "text/css" })); assertFalse(doTestCompression(10000, mimeTypes));
}
private boolean doTestCompression(int contentSize, String[] mimeTypes)
throws Exception {
String testContent = setUpFactoryForCompression(contentSize, mimeTypes);
TestGzipInputStreamFactory inputStreamFactory = new TestGzipInputStreamFactory();
Map<String, InputStreamFactory> contentDecoderMap = singletonMap("gzip",
(InputStreamFactory) inputStreamFactory);
String response = getResponse(getLocalUrl("/test.txt"),
new HttpComponentsClientHttpRequestFactory(HttpClientBuilder.create()
.setContentDecoderRegistry(contentDecoderMap).build()));
assertThat(response, equalTo(testContent));
return inputStreamFactory.wasCompressionUsed();
} }
protected String setUpFactoryForCompression(int contentSize, String[] mimeTypes) protected String setUpFactoryForCompression(int contentSize, String[] mimeTypes)
...@@ -549,9 +563,7 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests { ...@@ -549,9 +563,7 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
char[] chars = new char[contentSize]; char[] chars = new char[contentSize];
Arrays.fill(chars, 'F'); Arrays.fill(chars, 'F');
String testContent = new String(chars); String testContent = new String(chars);
AbstractEmbeddedServletContainerFactory factory = getFactory(); AbstractEmbeddedServletContainerFactory factory = getFactory();
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());
...@@ -561,47 +573,11 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests { ...@@ -561,47 +573,11 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
compression.setMimeTypes(mimeTypes); compression.setMimeTypes(mimeTypes);
} }
factory.setCompression(compression); factory.setCompression(compression);
this.container = factory.getEmbeddedServletContainer(); this.container = factory.getEmbeddedServletContainer();
this.container.start(); this.container.start();
return testContent; 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 (this.requested.get()) {
throw new IllegalStateException(
"On deflated InputStream already requested");
}
this.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) private void addTestTxtFile(AbstractEmbeddedServletContainerFactory factory)
throws IOException { throws IOException {
FileCopyUtils.copy("test", FileCopyUtils.copy("test",
...@@ -678,6 +654,26 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests { ...@@ -678,6 +654,26 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
return bean; return bean;
} }
private class TestGzipInputStreamFactory implements InputStreamFactory {
private final AtomicBoolean requested = new AtomicBoolean(false);
@Override
public InputStream create(InputStream instream) throws IOException {
if (this.requested.get()) {
throw new IllegalStateException(
"On deflated InputStream already requested");
}
this.requested.set(true);
return new GZIPInputStream(instream);
}
public boolean wasCompressionUsed() {
return this.requested.get();
}
}
@SuppressWarnings("serial") @SuppressWarnings("serial")
private static class InitCountingServlet extends GenericServlet { private static class InitCountingServlet extends GenericServlet {
...@@ -696,6 +692,7 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests { ...@@ -696,6 +692,7 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
public int getInitCount() { public int getInitCount() {
return this.initCount; return this.initCount;
} }
}; };
} }
...@@ -191,16 +191,13 @@ public class JettyEmbeddedServletContainerFactoryTests extends ...@@ -191,16 +191,13 @@ public class JettyEmbeddedServletContainerFactoryTests extends
char[] chars = new char[contentSize]; char[] chars = new char[contentSize];
Arrays.fill(chars, 'F'); Arrays.fill(chars, 'F');
final String testContent = new String(chars); final String testContent = new String(chars);
AbstractEmbeddedServletContainerFactory factory = getFactory(); AbstractEmbeddedServletContainerFactory factory = getFactory();
Compression compression = new Compression(); Compression compression = new Compression();
compression.setEnabled(true); compression.setEnabled(true);
if (mimeTypes != null) { if (mimeTypes != null) {
compression.setMimeTypes(mimeTypes); compression.setMimeTypes(mimeTypes);
} }
factory.setCompression(compression); factory.setCompression(compression);
this.container = factory.getEmbeddedServletContainer(new ServletRegistrationBean( this.container = factory.getEmbeddedServletContainer(new ServletRegistrationBean(
new HttpServlet() { new HttpServlet() {
@Override @Override
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment