From 46021928baa4dbb7f7cd4d5349e77324bcffc0f8 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Mon, 19 Feb 2018 23:58:58 -0800 Subject: [PATCH] Rework common server customization Update the configurable embedded web server factory interfaces to extend `ConfigurableWebServerFactory` so that the can be used in a `WebServerFactoryCustomizer`. Extract server specific customization to their own auto-configuration and align reactive/servlet server auto-configuration. Closes gh-8573 --- ...veManagementChildContextConfiguration.java | 22 +- ...ManagementWebServerFactoryCustomizer.java} | 52 +- ...etManagementChildContextConfiguration.java | 24 +- ...verFactoryCustomizerAutoConfiguration.java | 87 +++ ...a => JettyWebServerFactoryCustomizer.java} | 57 +- ... => TomcatWebServerFactoryCustomizer.java} | 127 ++-- ...> UndertowWebServerFactoryCustomizer.java} | 64 +- .../web/embedded/jetty/package-info.java | 22 - .../embedded/{undertow => }/package-info.java | 4 +- .../web/embedded/tomcat/package-info.java | 22 - ...tiveWebServerFactoryAutoConfiguration.java | 32 +- ...ReactiveWebServerFactoryConfiguration.java | 12 +- ...> ReactiveWebServerFactoryCustomizer.java} | 34 +- ...aultServletWebServerFactoryCustomizer.java | 136 ---- ...vletWebServerFactoryAutoConfiguration.java | 79 +- .../ServletWebServerFactoryConfiguration.java | 96 +++ .../ServletWebServerFactoryCustomizer.java | 69 ++ ...mcatServletWebServerFactoryCustomizer.java | 77 ++ .../web/servlet/WebMvcRegistrations.java | 4 +- .../servlet/WebMvcRegistrationsAdapter.java | 31 - .../main/resources/META-INF/spring.factories | 1 + .../JettyWebServerFactoryCustomizerTests.java | 160 +++++ ...TomcatWebServerFactoryCustomizerTests.java | 285 ++++++++ ...dertowWebServerFactoryCustomizerTests.java | 120 ++++ ...activeWebServerFactoryCustomizerTests.java | 538 -------------- ...ebServerFactoryAutoConfigurationTests.java | 2 +- ...activeWebServerFactoryCustomizerTests.java | 78 ++ ...ervletWebServerFactoryCustomizerTests.java | 677 ------------------ ...ervletWebServerFactoryCustomizerTests.java | 181 +++++ ...ervletWebServerFactoryCustomizerTests.java | 112 +++ .../BasicErrorControllerIntegrationTests.java | 3 +- .../BasicErrorControllerMockMvcTests.java | 3 +- .../src/checkstyle/import-control.xml | 10 +- .../ConfigurableJettyWebServerFactory.java | 6 +- .../ConfigurableTomcatWebServerFactory.java | 6 +- .../ConfigurableUndertowWebServerFactory.java | 7 +- 36 files changed, 1545 insertions(+), 1695 deletions(-) rename spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/server/{ManagementServerFactoryCustomizer.java => ManagementWebServerFactoryCustomizer.java} (64%) create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/EmbeddedWebServerFactoryCustomizerAutoConfiguration.java rename spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/{jetty/JettyCustomizer.java => JettyWebServerFactoryCustomizer.java} (76%) rename spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/{tomcat/TomcatCustomizer.java => TomcatWebServerFactoryCustomizer.java} (66%) rename spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/{undertow/UndertowCustomizer.java => UndertowWebServerFactoryCustomizer.java} (64%) delete mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/jetty/package-info.java rename spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/{undertow => }/package-info.java (82%) delete mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/tomcat/package-info.java rename spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/{DefaultReactiveWebServerFactoryCustomizer.java => ReactiveWebServerFactoryCustomizer.java} (51%) delete mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DefaultServletWebServerFactoryCustomizer.java create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryConfiguration.java create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryCustomizer.java create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/TomcatServletWebServerFactoryCustomizer.java delete mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcRegistrationsAdapter.java create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizerTests.java create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizerTests.java create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/UndertowWebServerFactoryCustomizerTests.java delete mode 100644 spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/DefaultReactiveWebServerFactoryCustomizerTests.java create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryCustomizerTests.java delete mode 100644 spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/DefaultServletWebServerFactoryCustomizerTests.java create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryCustomizerTests.java create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/TomcatServletWebServerFactoryCustomizerTests.java diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/reactive/ReactiveManagementChildContextConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/reactive/ReactiveManagementChildContextConfiguration.java index a2be93b9d20..df658c06c5f 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/reactive/ReactiveManagementChildContextConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/reactive/ReactiveManagementChildContextConfiguration.java @@ -19,10 +19,13 @@ package org.springframework.boot.actuate.autoconfigure.web.reactive; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration; import org.springframework.boot.actuate.autoconfigure.web.ManagementContextType; -import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerFactoryCustomizer; +import org.springframework.boot.actuate.autoconfigure.web.server.ManagementWebServerFactoryCustomizer; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; -import org.springframework.boot.autoconfigure.web.reactive.DefaultReactiveWebServerFactoryCustomizer; +import org.springframework.boot.autoconfigure.web.embedded.JettyWebServerFactoryCustomizer; +import org.springframework.boot.autoconfigure.web.embedded.TomcatWebServerFactoryCustomizer; +import org.springframework.boot.autoconfigure.web.embedded.UndertowWebServerFactoryCustomizer; +import org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryCustomizer; import org.springframework.boot.web.reactive.server.ConfigurableReactiveWebServerFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; @@ -44,9 +47,9 @@ import org.springframework.web.server.adapter.WebHttpHandlerBuilder; public class ReactiveManagementChildContextConfiguration { @Bean - public ReactiveManagementServerFactoryCustomizer reactiveManagementServerFactoryCustomizer( + public ReactiveManagementWebServerFactoryCustomizer reactiveManagementWebServerFactoryCustomizer( ListableBeanFactory beanFactory) { - return new ReactiveManagementServerFactoryCustomizer(beanFactory); + return new ReactiveManagementWebServerFactoryCustomizer(beanFactory); } @Bean @@ -54,11 +57,14 @@ public class ReactiveManagementChildContextConfiguration { return WebHttpHandlerBuilder.applicationContext(applicationContext).build(); } - class ReactiveManagementServerFactoryCustomizer extends - ManagementServerFactoryCustomizer { + class ReactiveManagementWebServerFactoryCustomizer extends + ManagementWebServerFactoryCustomizer { - ReactiveManagementServerFactoryCustomizer(ListableBeanFactory beanFactory) { - super(beanFactory, DefaultReactiveWebServerFactoryCustomizer.class); + ReactiveManagementWebServerFactoryCustomizer(ListableBeanFactory beanFactory) { + super(beanFactory, ReactiveWebServerFactoryCustomizer.class, + TomcatWebServerFactoryCustomizer.class, + JettyWebServerFactoryCustomizer.class, + UndertowWebServerFactoryCustomizer.class); } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/server/ManagementServerFactoryCustomizer.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/server/ManagementWebServerFactoryCustomizer.java similarity index 64% rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/server/ManagementServerFactoryCustomizer.java rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/server/ManagementWebServerFactoryCustomizer.java index c1a626a1188..5716b601d92 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/server/ManagementServerFactoryCustomizer.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/server/ManagementWebServerFactoryCustomizer.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -16,11 +16,17 @@ package org.springframework.boot.actuate.autoconfigure.web.server; +import java.util.Arrays; import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.ListableBeanFactory; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.boot.util.LambdaSafe; import org.springframework.boot.web.server.ConfigurableWebServerFactory; import org.springframework.boot.web.server.ErrorPage; import org.springframework.boot.web.server.Ssl; @@ -36,17 +42,18 @@ import org.springframework.core.Ordered; * @author Andy Wilkinson * @since 2.0.0 */ -public abstract class ManagementServerFactoryCustomizer +public abstract class ManagementWebServerFactoryCustomizer implements WebServerFactoryCustomizer, Ordered { private final ListableBeanFactory beanFactory; - private final Class> customizerClass; + private final Class>[] customizerClasses; - protected ManagementServerFactoryCustomizer(ListableBeanFactory beanFactory, - Class> customizerClass) { + @SafeVarargs + protected ManagementWebServerFactoryCustomizer(ListableBeanFactory beanFactory, + Class>... customizerClasses) { this.beanFactory = beanFactory; - this.customizerClass = customizerClass; + this.customizerClasses = customizerClasses; } @Override @@ -59,19 +66,42 @@ public abstract class ManagementServerFactoryCustomizer webServerFactoryCustomizer = BeanFactoryUtils - .beanOfTypeIncludingAncestors(this.beanFactory, this.customizerClass); // Customize as per the parent context first (so e.g. the access logs go to // the same place) - webServerFactoryCustomizer.customize(factory); + customizeSameAsParentContext(factory); // Then reset the error pages factory.setErrorPages(Collections.emptySet()); // and add the management-specific bits + ServerProperties serverProperties = BeanFactoryUtils + .beanOfTypeIncludingAncestors(this.beanFactory, ServerProperties.class); customize(factory, managementServerProperties, serverProperties); } + private void customizeSameAsParentContext(T factory) { + List> customizers = Arrays + .stream(this.customizerClasses).map(this::getCustomizer) + .filter(Objects::nonNull).collect(Collectors.toList()); + invokeCustomizers(factory, customizers); + } + + private WebServerFactoryCustomizer getCustomizer( + Class> customizerClass) { + try { + return BeanFactoryUtils.beanOfTypeIncludingAncestors(this.beanFactory, + customizerClass); + } + catch (NoSuchBeanDefinitionException ex) { + return null; + } + } + + @SuppressWarnings("unchecked") + private void invokeCustomizers(T factory, + List> customizers) { + LambdaSafe.callbacks(WebServerFactoryCustomizer.class, customizers, factory) + .invoke((customizer) -> customizer.customize(factory)); + } + protected void customize(T factory, ManagementServerProperties managementServerProperties, ServerProperties serverProperties) { diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/servlet/ServletManagementChildContextConfiguration.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/servlet/ServletManagementChildContextConfiguration.java index dd68a9175d0..2260d308aa9 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/servlet/ServletManagementChildContextConfiguration.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/servlet/ServletManagementChildContextConfiguration.java @@ -26,15 +26,19 @@ import org.springframework.beans.factory.HierarchicalBeanFactory; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration; import org.springframework.boot.actuate.autoconfigure.web.ManagementContextType; -import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerFactoryCustomizer; import org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties; +import org.springframework.boot.actuate.autoconfigure.web.server.ManagementWebServerFactoryCustomizer; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.boot.autoconfigure.web.ServerProperties; -import org.springframework.boot.autoconfigure.web.servlet.DefaultServletWebServerFactoryCustomizer; +import org.springframework.boot.autoconfigure.web.embedded.JettyWebServerFactoryCustomizer; +import org.springframework.boot.autoconfigure.web.embedded.TomcatWebServerFactoryCustomizer; +import org.springframework.boot.autoconfigure.web.embedded.UndertowWebServerFactoryCustomizer; +import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryCustomizer; +import org.springframework.boot.autoconfigure.web.servlet.TomcatServletWebServerFactoryCustomizer; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory; import org.springframework.boot.web.server.WebServerFactoryCustomizer; @@ -61,9 +65,9 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe class ServletManagementChildContextConfiguration { @Bean - public ServletManagementServerFactoryCustomizer serverFactoryCustomizer( + public ServletManagementWebServerFactoryCustomizer servletManagementWebServerFactoryCustomizer( ListableBeanFactory beanFactory) { - return new ServletManagementServerFactoryCustomizer(beanFactory); + return new ServletManagementWebServerFactoryCustomizer(beanFactory); } @Bean @@ -90,11 +94,15 @@ class ServletManagementChildContextConfiguration { } - static class ServletManagementServerFactoryCustomizer extends - ManagementServerFactoryCustomizer { + static class ServletManagementWebServerFactoryCustomizer extends + ManagementWebServerFactoryCustomizer { - ServletManagementServerFactoryCustomizer(ListableBeanFactory beanFactory) { - super(beanFactory, DefaultServletWebServerFactoryCustomizer.class); + ServletManagementWebServerFactoryCustomizer(ListableBeanFactory beanFactory) { + super(beanFactory, ServletWebServerFactoryCustomizer.class, + TomcatServletWebServerFactoryCustomizer.class, + TomcatWebServerFactoryCustomizer.class, + JettyWebServerFactoryCustomizer.class, + UndertowWebServerFactoryCustomizer.class); } @Override diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/EmbeddedWebServerFactoryCustomizerAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/EmbeddedWebServerFactoryCustomizerAutoConfiguration.java new file mode 100644 index 00000000000..25a3cc66278 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/EmbeddedWebServerFactoryCustomizerAutoConfiguration.java @@ -0,0 +1,87 @@ +/* + * Copyright 2012-2018 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.embedded; + +import io.undertow.Undertow; +import org.apache.catalina.startup.Tomcat; +import org.apache.coyote.UpgradeProtocol; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.Loader; +import org.eclipse.jetty.webapp.WebAppContext; +import org.xnio.SslClientAuthMode; + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for embedded servlet and reactive + * web servers customizations. + * + * @author Phillip Webb + * @since 2.0.0 + */ +@Configuration +@EnableConfigurationProperties(ServerProperties.class) +public class EmbeddedWebServerFactoryCustomizerAutoConfiguration { + + @ConditionalOnClass({ Tomcat.class, UpgradeProtocol.class }) + public static class TomcatWebServerFactoryCustomizerConfiguration { + + @Bean + public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer( + Environment environment, ServerProperties serverProperties) { + return new TomcatWebServerFactoryCustomizer(environment, serverProperties); + } + + } + + /** + * Nested configuration if Jetty is being used. + */ + @Configuration + @ConditionalOnClass({ Server.class, Loader.class, WebAppContext.class }) + public static class JettyWebServerFactoryCustomizerConfiguration { + + @Bean + public JettyWebServerFactoryCustomizer jettyWebServerFactoryCustomizer( + Environment environment, ServerProperties serverProperties) { + return new JettyWebServerFactoryCustomizer(environment, serverProperties); + } + + } + + /** + * Nested configuration if Undertow is being used. + */ + @Configuration + @ConditionalOnClass({ Undertow.class, SslClientAuthMode.class }) + public static class UndertowWebServerFactoryCustomizerConfiguration { + + @Bean + public UndertowWebServerFactoryCustomizer undertowWebServerFactoryCustomizer( + Environment environment, ServerProperties serverProperties) { + return new UndertowWebServerFactoryCustomizer(environment, serverProperties); + } + + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/jetty/JettyCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizer.java similarity index 76% rename from spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/jetty/JettyCustomizer.java rename to spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizer.java index b266e381f96..c50a111c57e 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/jetty/JettyCustomizer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.autoconfigure.web.embedded.jetty; +package org.springframework.boot.autoconfigure.web.embedded; import java.time.Duration; @@ -33,38 +33,53 @@ import org.springframework.boot.cloud.CloudPlatform; import org.springframework.boot.context.properties.PropertyMapper; import org.springframework.boot.web.embedded.jetty.ConfigurableJettyWebServerFactory; import org.springframework.boot.web.embedded.jetty.JettyServerCustomizer; +import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.core.Ordered; import org.springframework.core.env.Environment; /** * Customization for Jetty-specific features common for both Servlet and Reactive servers. * * @author Brian Clozel + * @author Phillip Webb * @since 2.0.0 */ -public final class JettyCustomizer { +public class JettyWebServerFactoryCustomizer implements + WebServerFactoryCustomizer, Ordered { - private JettyCustomizer() { + private final Environment environment; + + private final ServerProperties serverProperties; + + public JettyWebServerFactoryCustomizer(Environment environment, + ServerProperties serverProperties) { + this.environment = environment; + this.serverProperties = serverProperties; } - public static void customizeJetty(ServerProperties serverProperties, - Environment environment, ConfigurableJettyWebServerFactory factory) { - ServerProperties.Jetty jettyProperties = serverProperties.getJetty(); + @Override + public int getOrder() { + return 0; + } + + @Override + public void customize(ConfigurableJettyWebServerFactory factory) { + ServerProperties properties = this.serverProperties; + ServerProperties.Jetty jettyProperties = properties.getJetty(); factory.setUseForwardHeaders( - getOrDeduceUseForwardHeaders(serverProperties, environment)); + getOrDeduceUseForwardHeaders(properties, this.environment)); PropertyMapper propertyMapper = PropertyMapper.get(); propertyMapper.from(jettyProperties::getAcceptors).whenNonNull() .to(factory::setAcceptors); propertyMapper.from(jettyProperties::getSelectors).whenNonNull() .to(factory::setSelectors); - propertyMapper.from(serverProperties::getMaxHttpHeaderSize) - .when(JettyCustomizer::isPositive) + propertyMapper.from(properties::getMaxHttpHeaderSize).when(this::isPositive) .to((maxHttpHeaderSize) -> customizeMaxHttpHeaderSize(factory, maxHttpHeaderSize)); - propertyMapper.from(jettyProperties::getMaxHttpPostSize) - .when(JettyCustomizer::isPositive) + propertyMapper.from(jettyProperties::getMaxHttpPostSize).when(this::isPositive) .to((maxHttpPostSize) -> customizeMaxHttpPostSize(factory, maxHttpPostSize)); - propertyMapper.from(serverProperties::getConnectionTimeout).whenNonNull() + propertyMapper.from(properties::getConnectionTimeout).whenNonNull() .to((connectionTimeout) -> customizeConnectionTimeout(factory, connectionTimeout)); propertyMapper.from(jettyProperties::getAccesslog) @@ -72,11 +87,11 @@ public final class JettyCustomizer { .to((accesslog) -> customizeAccessLog(factory, accesslog)); } - private static boolean isPositive(Integer value) { + private boolean isPositive(Integer value) { return value > 0; } - private static boolean getOrDeduceUseForwardHeaders(ServerProperties serverProperties, + private boolean getOrDeduceUseForwardHeaders(ServerProperties serverProperties, Environment environment) { if (serverProperties.isUseForwardHeaders() != null) { return serverProperties.isUseForwardHeaders(); @@ -85,8 +100,8 @@ public final class JettyCustomizer { return platform != null && platform.isUsingForwardHeaders(); } - private static void customizeConnectionTimeout( - ConfigurableJettyWebServerFactory factory, Duration connectionTimeout) { + private void customizeConnectionTimeout(ConfigurableJettyWebServerFactory factory, + Duration connectionTimeout) { factory.addServerCustomizers((server) -> { for (org.eclipse.jetty.server.Connector connector : server.getConnectors()) { if (connector instanceof AbstractConnector) { @@ -97,8 +112,8 @@ public final class JettyCustomizer { }); } - private static void customizeMaxHttpHeaderSize( - ConfigurableJettyWebServerFactory factory, int maxHttpHeaderSize) { + private void customizeMaxHttpHeaderSize(ConfigurableJettyWebServerFactory factory, + int maxHttpHeaderSize) { factory.addServerCustomizers(new JettyServerCustomizer() { @Override @@ -124,8 +139,8 @@ public final class JettyCustomizer { }); } - private static void customizeMaxHttpPostSize( - ConfigurableJettyWebServerFactory factory, int maxHttpPostSize) { + private void customizeMaxHttpPostSize(ConfigurableJettyWebServerFactory factory, + int maxHttpPostSize) { factory.addServerCustomizers(new JettyServerCustomizer() { @Override @@ -153,7 +168,7 @@ public final class JettyCustomizer { }); } - private static void customizeAccessLog(ConfigurableJettyWebServerFactory factory, + private void customizeAccessLog(ConfigurableJettyWebServerFactory factory, ServerProperties.Jetty.Accesslog properties) { factory.addServerCustomizers((server) -> { NCSARequestLog log = new NCSARequestLog(); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/tomcat/TomcatCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizer.java similarity index 66% rename from spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/tomcat/TomcatCustomizer.java rename to spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizer.java index 3d49a1f2d31..0e02cdfad9f 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/tomcat/TomcatCustomizer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.autoconfigure.web.embedded.tomcat; +package org.springframework.boot.autoconfigure.web.embedded; import java.time.Duration; @@ -29,9 +29,12 @@ import org.apache.coyote.http11.AbstractHttp11Protocol; import org.springframework.boot.autoconfigure.web.ErrorProperties; import org.springframework.boot.autoconfigure.web.ErrorProperties.IncludeStacktrace; import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.boot.autoconfigure.web.ServerProperties.Tomcat; import org.springframework.boot.cloud.CloudPlatform; import org.springframework.boot.context.properties.PropertyMapper; import org.springframework.boot.web.embedded.tomcat.ConfigurableTomcatWebServerFactory; +import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.core.Ordered; import org.springframework.core.env.Environment; import org.springframework.util.StringUtils; @@ -42,34 +45,44 @@ import org.springframework.util.StringUtils; * @author Brian Clozel * @author Yulin Qin * @author Stephane Nicoll + * @author Phillip Webb * @since 2.0.0 */ -public final class TomcatCustomizer { +public class TomcatWebServerFactoryCustomizer implements + WebServerFactoryCustomizer, Ordered { - private TomcatCustomizer() { + private final Environment environment; + + private final ServerProperties serverProperties; + + public TomcatWebServerFactoryCustomizer(Environment environment, + ServerProperties serverProperties) { + this.environment = environment; + this.serverProperties = serverProperties; } - public static void customizeTomcat(ServerProperties serverProperties, - Environment environment, ConfigurableTomcatWebServerFactory factory) { - ServerProperties.Tomcat tomcatProperties = serverProperties.getTomcat(); + @Override + public int getOrder() { + return 0; + } + + @Override + public void customize(ConfigurableTomcatWebServerFactory factory) { + ServerProperties properties = this.serverProperties; + ServerProperties.Tomcat tomcatProperties = properties.getTomcat(); PropertyMapper propertyMapper = PropertyMapper.get(); propertyMapper.from(tomcatProperties::getBasedir).whenNonNull() .to(factory::setBaseDirectory); propertyMapper.from(tomcatProperties::getBackgroundProcessorDelay).whenNonNull() .as(Duration::getSeconds).as(Long::intValue) .to(factory::setBackgroundProcessorDelay); - customizeRemoteIpValve(serverProperties, environment, factory); - propertyMapper.from(tomcatProperties::getMaxThreads) - .when(TomcatCustomizer::isPositive) + customizeRemoteIpValve(factory); + propertyMapper.from(tomcatProperties::getMaxThreads).when(this::isPositive) .to((maxThreads) -> customizeMaxThreads(factory, tomcatProperties.getMaxThreads())); - propertyMapper.from(tomcatProperties::getMinSpareThreads) - .when(TomcatCustomizer::isPositive) + propertyMapper.from(tomcatProperties::getMinSpareThreads).when(this::isPositive) .to((minSpareThreads) -> customizeMinThreads(factory, minSpareThreads)); - propertyMapper - .from(() -> determineMaxHttpHeaderSize(serverProperties, - tomcatProperties)) - .when(TomcatCustomizer::isPositive) + propertyMapper.from(() -> determineMaxHttpHeaderSize()).when(this::isPositive) .to((maxHttpHeaderSize) -> customizeMaxHttpHeaderSize(factory, maxHttpHeaderSize)); propertyMapper.from(tomcatProperties::getMaxHttpPostSize) @@ -78,34 +91,31 @@ public final class TomcatCustomizer { maxHttpPostSize)); propertyMapper.from(tomcatProperties::getAccesslog) .when(ServerProperties.Tomcat.Accesslog::isEnabled) - .to((enabled) -> customizeAccessLog(tomcatProperties, factory)); + .to((enabled) -> customizeAccessLog(factory)); propertyMapper.from(tomcatProperties::getUriEncoding).whenNonNull() .to(factory::setUriEncoding); - propertyMapper.from(serverProperties::getConnectionTimeout).whenNonNull() + propertyMapper.from(properties::getConnectionTimeout).whenNonNull() .to((connectionTimeout) -> customizeConnectionTimeout(factory, connectionTimeout)); - propertyMapper.from(tomcatProperties::getMaxConnections) - .when(TomcatCustomizer::isPositive) + propertyMapper.from(tomcatProperties::getMaxConnections).when(this::isPositive) .to((maxConnections) -> customizeMaxConnections(factory, maxConnections)); - propertyMapper.from(tomcatProperties::getAcceptCount) - .when(TomcatCustomizer::isPositive) + propertyMapper.from(tomcatProperties::getAcceptCount).when(this::isPositive) .to((acceptCount) -> customizeAcceptCount(factory, acceptCount)); - customizeStaticResources(serverProperties.getTomcat().getResource(), factory); - customizeErrorReportValve(serverProperties.getError(), factory); + customizeStaticResources(factory); + customizeErrorReportValve(properties.getError(), factory); } - private static boolean isPositive(int value) { + private boolean isPositive(int value) { return value > 0; } - private static int determineMaxHttpHeaderSize(ServerProperties serverProperties, - ServerProperties.Tomcat tomcatProperties) { - return serverProperties.getMaxHttpHeaderSize() > 0 - ? serverProperties.getMaxHttpHeaderSize() - : tomcatProperties.getMaxHttpHeaderSize(); + private int determineMaxHttpHeaderSize() { + return this.serverProperties.getMaxHttpHeaderSize() > 0 + ? this.serverProperties.getMaxHttpHeaderSize() + : this.serverProperties.getTomcat().getMaxHttpHeaderSize(); } - private static void customizeAcceptCount(ConfigurableTomcatWebServerFactory factory, + private void customizeAcceptCount(ConfigurableTomcatWebServerFactory factory, int acceptCount) { factory.addConnectorCustomizers((connector) -> { ProtocolHandler handler = connector.getProtocolHandler(); @@ -116,8 +126,8 @@ public final class TomcatCustomizer { }); } - private static void customizeMaxConnections( - ConfigurableTomcatWebServerFactory factory, int maxConnections) { + private void customizeMaxConnections(ConfigurableTomcatWebServerFactory factory, + int maxConnections) { factory.addConnectorCustomizers((connector) -> { ProtocolHandler handler = connector.getProtocolHandler(); if (handler instanceof AbstractProtocol) { @@ -127,8 +137,8 @@ public final class TomcatCustomizer { }); } - private static void customizeConnectionTimeout( - ConfigurableTomcatWebServerFactory factory, Duration connectionTimeout) { + private void customizeConnectionTimeout(ConfigurableTomcatWebServerFactory factory, + Duration connectionTimeout) { factory.addConnectorCustomizers((connector) -> { ProtocolHandler handler = connector.getProtocolHandler(); if (handler instanceof AbstractProtocol) { @@ -138,13 +148,13 @@ public final class TomcatCustomizer { }); } - private static void customizeRemoteIpValve(ServerProperties properties, - Environment environment, ConfigurableTomcatWebServerFactory factory) { - String protocolHeader = properties.getTomcat().getProtocolHeader(); - String remoteIpHeader = properties.getTomcat().getRemoteIpHeader(); + private void customizeRemoteIpValve(ConfigurableTomcatWebServerFactory factory) { + Tomcat tomcatProperties = this.serverProperties.getTomcat(); + String protocolHeader = tomcatProperties.getProtocolHeader(); + String remoteIpHeader = tomcatProperties.getRemoteIpHeader(); // For back compatibility the valve is also enabled if protocol-header is set if (StringUtils.hasText(protocolHeader) || StringUtils.hasText(remoteIpHeader) - || getOrDeduceUseForwardHeaders(properties, environment)) { + || getOrDeduceUseForwardHeaders()) { RemoteIpValve valve = new RemoteIpValve(); valve.setProtocolHeader(StringUtils.hasLength(protocolHeader) ? protocolHeader : "X-Forwarded-Proto"); @@ -153,26 +163,25 @@ public final class TomcatCustomizer { } // The internal proxies default to a white list of "safe" internal IP // addresses - valve.setInternalProxies(properties.getTomcat().getInternalProxies()); - valve.setPortHeader(properties.getTomcat().getPortHeader()); + valve.setInternalProxies(tomcatProperties.getInternalProxies()); + valve.setPortHeader(tomcatProperties.getPortHeader()); valve.setProtocolHeaderHttpsValue( - properties.getTomcat().getProtocolHeaderHttpsValue()); + tomcatProperties.getProtocolHeaderHttpsValue()); // ... so it's safe to add this valve by default. factory.addEngineValves(valve); } } - private static boolean getOrDeduceUseForwardHeaders(ServerProperties serverProperties, - Environment environment) { - if (serverProperties.isUseForwardHeaders() != null) { - return serverProperties.isUseForwardHeaders(); + private boolean getOrDeduceUseForwardHeaders() { + if (this.serverProperties.isUseForwardHeaders() != null) { + return this.serverProperties.isUseForwardHeaders(); } - CloudPlatform platform = CloudPlatform.getActive(environment); + CloudPlatform platform = CloudPlatform.getActive(this.environment); return platform != null && platform.isUsingForwardHeaders(); } @SuppressWarnings("rawtypes") - private static void customizeMaxThreads(ConfigurableTomcatWebServerFactory factory, + private void customizeMaxThreads(ConfigurableTomcatWebServerFactory factory, int maxThreads) { factory.addConnectorCustomizers((connector) -> { ProtocolHandler handler = connector.getProtocolHandler(); @@ -184,7 +193,7 @@ public final class TomcatCustomizer { } @SuppressWarnings("rawtypes") - private static void customizeMinThreads(ConfigurableTomcatWebServerFactory factory, + private void customizeMinThreads(ConfigurableTomcatWebServerFactory factory, int minSpareThreads) { factory.addConnectorCustomizers((connector) -> { ProtocolHandler handler = connector.getProtocolHandler(); @@ -196,8 +205,8 @@ public final class TomcatCustomizer { } @SuppressWarnings("rawtypes") - private static void customizeMaxHttpHeaderSize( - ConfigurableTomcatWebServerFactory factory, int maxHttpHeaderSize) { + private void customizeMaxHttpHeaderSize(ConfigurableTomcatWebServerFactory factory, + int maxHttpHeaderSize) { factory.addConnectorCustomizers((connector) -> { ProtocolHandler handler = connector.getProtocolHandler(); if (handler instanceof AbstractHttp11Protocol) { @@ -207,14 +216,14 @@ public final class TomcatCustomizer { }); } - private static void customizeMaxHttpPostSize( - ConfigurableTomcatWebServerFactory factory, int maxHttpPostSize) { + private void customizeMaxHttpPostSize(ConfigurableTomcatWebServerFactory factory, + int maxHttpPostSize) { factory.addConnectorCustomizers( (connector) -> connector.setMaxPostSize(maxHttpPostSize)); } - private static void customizeAccessLog(ServerProperties.Tomcat tomcatProperties, - ConfigurableTomcatWebServerFactory factory) { + private void customizeAccessLog(ConfigurableTomcatWebServerFactory factory) { + ServerProperties.Tomcat tomcatProperties = this.serverProperties.getTomcat(); AccessLogValve valve = new AccessLogValve(); valve.setPattern(tomcatProperties.getAccesslog().getPattern()); valve.setDirectory(tomcatProperties.getAccesslog().getDirectory()); @@ -229,9 +238,9 @@ public final class TomcatCustomizer { factory.addEngineValves(valve); } - private static void customizeStaticResources( - ServerProperties.Tomcat.Resource resource, - ConfigurableTomcatWebServerFactory factory) { + private void customizeStaticResources(ConfigurableTomcatWebServerFactory factory) { + ServerProperties.Tomcat.Resource resource = this.serverProperties.getTomcat() + .getResource(); if (resource.getCacheTtl() == null) { return; } @@ -245,7 +254,7 @@ public final class TomcatCustomizer { }); } - private static void customizeErrorReportValve(ErrorProperties error, + private void customizeErrorReportValve(ErrorProperties error, ConfigurableTomcatWebServerFactory factory) { if (error.getIncludeStacktrace() == IncludeStacktrace.NEVER) { factory.addContextCustomizers((context) -> { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/undertow/UndertowCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/UndertowWebServerFactoryCustomizer.java similarity index 64% rename from spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/undertow/UndertowCustomizer.java rename to spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/UndertowWebServerFactoryCustomizer.java index 0c855f93a43..9c022e42506 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/undertow/UndertowCustomizer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/UndertowWebServerFactoryCustomizer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.autoconfigure.web.embedded.undertow; +package org.springframework.boot.autoconfigure.web.embedded; import java.time.Duration; @@ -24,6 +24,8 @@ import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.cloud.CloudPlatform; import org.springframework.boot.context.properties.PropertyMapper; import org.springframework.boot.web.embedded.undertow.ConfigurableUndertowWebServerFactory; +import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.core.Ordered; import org.springframework.core.env.Environment; /** @@ -33,15 +35,31 @@ import org.springframework.core.env.Environment; * @author Brian Clozel * @author Yulin Qin * @author Stephane Nicoll + * @author Phillip Webb + * @since 2.0.0 */ -public final class UndertowCustomizer { +public class UndertowWebServerFactoryCustomizer implements + WebServerFactoryCustomizer, Ordered { - private UndertowCustomizer() { + private final Environment environment; + + private final ServerProperties serverProperties; + + public UndertowWebServerFactoryCustomizer(Environment environment, + ServerProperties serverProperties) { + this.environment = environment; + this.serverProperties = serverProperties; } - public static void customizeUndertow(ServerProperties serverProperties, - Environment environment, ConfigurableUndertowWebServerFactory factory) { - ServerProperties.Undertow undertowProperties = serverProperties.getUndertow(); + @Override + public int getOrder() { + return 0; + } + + @Override + public void customize(ConfigurableUndertowWebServerFactory factory) { + ServerProperties properties = this.serverProperties; + ServerProperties.Undertow undertowProperties = properties.getUndertow(); ServerProperties.Undertow.Accesslog accesslogProperties = undertowProperties .getAccesslog(); PropertyMapper propertyMapper = PropertyMapper.get().alwaysApplyingWhenNonNull(); @@ -63,52 +81,48 @@ public final class UndertowCustomizer { .to(factory::setAccessLogSuffix); propertyMapper.from(accesslogProperties::isRotate) .to(factory::setAccessLogRotate); - propertyMapper - .from(() -> getOrDeduceUseForwardHeaders(serverProperties, environment)) + propertyMapper.from(() -> getOrDeduceUseForwardHeaders()) .to(factory::setUseForwardHeaders); - propertyMapper.from(serverProperties::getMaxHttpHeaderSize) - .when(UndertowCustomizer::isPositive) + propertyMapper.from(properties::getMaxHttpHeaderSize).when(this::isPositive) .to((maxHttpHeaderSize) -> customizeMaxHttpHeaderSize(factory, maxHttpHeaderSize)); - propertyMapper.from(undertowProperties::getMaxHttpPostSize) - .when(UndertowCustomizer::isPositive) + propertyMapper.from(undertowProperties::getMaxHttpPostSize).when(this::isPositive) .to((maxHttpPostSize) -> customizeMaxHttpPostSize(factory, maxHttpPostSize)); - propertyMapper.from(serverProperties::getConnectionTimeout) + propertyMapper.from(properties::getConnectionTimeout) .to((connectionTimeout) -> customizeConnectionTimeout(factory, connectionTimeout)); factory.addDeploymentInfoCustomizers((deploymentInfo) -> deploymentInfo .setEagerFilterInit(undertowProperties.isEagerFilterInit())); } - private static boolean isPositive(Number value) { + private boolean isPositive(Number value) { return value.longValue() > 0; } - private static void customizeConnectionTimeout( - ConfigurableUndertowWebServerFactory factory, Duration connectionTimeout) { + private void customizeConnectionTimeout(ConfigurableUndertowWebServerFactory factory, + Duration connectionTimeout) { factory.addBuilderCustomizers((builder) -> builder.setSocketOption( UndertowOptions.NO_REQUEST_TIMEOUT, (int) connectionTimeout.toMillis())); } - private static void customizeMaxHttpHeaderSize( - ConfigurableUndertowWebServerFactory factory, int maxHttpHeaderSize) { + private void customizeMaxHttpHeaderSize(ConfigurableUndertowWebServerFactory factory, + int maxHttpHeaderSize) { factory.addBuilderCustomizers((builder) -> builder .setServerOption(UndertowOptions.MAX_HEADER_SIZE, maxHttpHeaderSize)); } - private static void customizeMaxHttpPostSize( - ConfigurableUndertowWebServerFactory factory, long maxHttpPostSize) { + private void customizeMaxHttpPostSize(ConfigurableUndertowWebServerFactory factory, + long maxHttpPostSize) { factory.addBuilderCustomizers((builder) -> builder .setServerOption(UndertowOptions.MAX_ENTITY_SIZE, maxHttpPostSize)); } - private static boolean getOrDeduceUseForwardHeaders(ServerProperties serverProperties, - Environment environment) { - if (serverProperties.isUseForwardHeaders() != null) { - return serverProperties.isUseForwardHeaders(); + private boolean getOrDeduceUseForwardHeaders() { + if (this.serverProperties.isUseForwardHeaders() != null) { + return this.serverProperties.isUseForwardHeaders(); } - CloudPlatform platform = CloudPlatform.getActive(environment); + CloudPlatform platform = CloudPlatform.getActive(this.environment); return platform != null && platform.isUsingForwardHeaders(); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/jetty/package-info.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/jetty/package-info.java deleted file mode 100644 index 48e2b24335f..00000000000 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/jetty/package-info.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2012-2018 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. - */ - -/** - * Configuration for embedded reactive and servlet Jetty web servers. - * - * @see org.springframework.boot.web.embedded.jetty.ConfigurableJettyWebServerFactory - */ -package org.springframework.boot.autoconfigure.web.embedded.jetty; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/undertow/package-info.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/package-info.java similarity index 82% rename from spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/undertow/package-info.java rename to spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/package-info.java index 823103ed17f..203bf79c8e7 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/undertow/package-info.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/package-info.java @@ -15,6 +15,6 @@ */ /** - * Support classes for the auto-configuration of embedded Undertow. + * Configuration for embedded reactive and servlet web servers. */ -package org.springframework.boot.autoconfigure.web.embedded.undertow; +package org.springframework.boot.autoconfigure.web.embedded; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/tomcat/package-info.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/tomcat/package-info.java deleted file mode 100644 index c20886aee76..00000000000 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/tomcat/package-info.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2012-2018 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. - */ - -/** - * Configuration for embedded reactive and servlet Tomcat web servers. - * - * @see org.springframework.boot.web.embedded.tomcat.ConfigurableTomcatWebServerFactory - */ -package org.springframework.boot.autoconfigure.web.embedded.tomcat; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryAutoConfiguration.java index bae41a42eb9..d08b561542b 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryAutoConfiguration.java @@ -25,7 +25,6 @@ import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.boot.autoconfigure.AutoConfigureOrder; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -51,17 +50,16 @@ import org.springframework.util.ObjectUtils; @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) @EnableConfigurationProperties(ServerProperties.class) @Import({ ReactiveWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, - ReactiveWebServerFactoryConfiguration.TomcatConfiguration.class, - ReactiveWebServerFactoryConfiguration.JettyConfiguration.class, - ReactiveWebServerFactoryConfiguration.UndertowConfiguration.class, - ReactiveWebServerFactoryConfiguration.ReactorNettyConfiguration.class }) + ReactiveWebServerFactoryConfiguration.EmbeddedNetty.class, + ReactiveWebServerFactoryConfiguration.EmbeddedTomcat.class, + ReactiveWebServerFactoryConfiguration.EmbeddedJetty.class, + ReactiveWebServerFactoryConfiguration.EmbeddedUndertow.class }) public class ReactiveWebServerFactoryAutoConfiguration { - @ConditionalOnMissingBean @Bean - public DefaultReactiveWebServerFactoryCustomizer defaultReactiveWebServerCustomizer( + public ReactiveWebServerFactoryCustomizer reactiveWebServerFactoryCustomizer( ServerProperties serverProperties) { - return new DefaultReactiveWebServerFactoryCustomizer(serverProperties); + return new ReactiveWebServerFactoryCustomizer(serverProperties); } /** @@ -86,14 +84,18 @@ public class ReactiveWebServerFactoryAutoConfiguration { if (this.beanFactory == null) { return; } - if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType( - WebServerFactoryCustomizerBeanPostProcessor.class, true, false))) { - RootBeanDefinition beanDefinition = new RootBeanDefinition( - WebServerFactoryCustomizerBeanPostProcessor.class); - beanDefinition.setSynthetic(true); - registry.registerBeanDefinition( - "webServerFactoryCustomizerBeanPostProcessor", beanDefinition); + registerSyntheticBeanIfMissing(registry, + "webServerFactoryCustomizerBeanPostProcessor", + WebServerFactoryCustomizerBeanPostProcessor.class); + } + private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, + String name, Class beanClass) { + if (ObjectUtils.isEmpty( + this.beanFactory.getBeanNamesForType(beanClass, true, false))) { + RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass); + beanDefinition.setSynthetic(true); + registry.registerBeanDefinition(name, beanDefinition); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryConfiguration.java index c84db55e209..01644a9b8e9 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryConfiguration.java @@ -27,6 +27,7 @@ import org.springframework.boot.web.embedded.tomcat.TomcatReactiveWebServerFacto import org.springframework.boot.web.embedded.undertow.UndertowReactiveWebServerFactory; import org.springframework.boot.web.reactive.server.ReactiveWebServerFactory; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; /** * Configuration classes for reactive web servers @@ -38,9 +39,10 @@ import org.springframework.context.annotation.Bean; */ abstract class ReactiveWebServerFactoryConfiguration { + @Configuration @ConditionalOnMissingBean(ReactiveWebServerFactory.class) @ConditionalOnClass({ HttpServer.class }) - static class ReactorNettyConfiguration { + static class EmbeddedNetty { @Bean public NettyReactiveWebServerFactory NettyReactiveWebServerFactory() { @@ -49,9 +51,10 @@ abstract class ReactiveWebServerFactoryConfiguration { } + @Configuration @ConditionalOnMissingBean(ReactiveWebServerFactory.class) @ConditionalOnClass({ org.apache.catalina.startup.Tomcat.class }) - static class TomcatConfiguration { + static class EmbeddedTomcat { @Bean public TomcatReactiveWebServerFactory tomcatReactiveWebServerFactory() { @@ -60,9 +63,10 @@ abstract class ReactiveWebServerFactoryConfiguration { } + @Configuration @ConditionalOnMissingBean(ReactiveWebServerFactory.class) @ConditionalOnClass({ org.eclipse.jetty.server.Server.class }) - static class JettyConfiguration { + static class EmbeddedJetty { @Bean public JettyReactiveWebServerFactory jettyReactiveWebServerFactory() { @@ -73,7 +77,7 @@ abstract class ReactiveWebServerFactoryConfiguration { @ConditionalOnMissingBean(ReactiveWebServerFactory.class) @ConditionalOnClass({ Undertow.class }) - static class UndertowConfiguration { + static class EmbeddedUndertow { @Bean public UndertowReactiveWebServerFactory undertowReactiveWebServerFactory() { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/DefaultReactiveWebServerFactoryCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryCustomizer.java similarity index 51% rename from spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/DefaultReactiveWebServerFactoryCustomizer.java rename to spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryCustomizer.java index b84acacb8b6..46f2e34cc5c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/DefaultReactiveWebServerFactoryCustomizer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryCustomizer.java @@ -17,35 +17,25 @@ package org.springframework.boot.autoconfigure.web.reactive; import org.springframework.boot.autoconfigure.web.ServerProperties; -import org.springframework.boot.autoconfigure.web.embedded.jetty.JettyCustomizer; -import org.springframework.boot.autoconfigure.web.embedded.tomcat.TomcatCustomizer; -import org.springframework.boot.autoconfigure.web.embedded.undertow.UndertowCustomizer; import org.springframework.boot.context.properties.PropertyMapper; -import org.springframework.boot.web.embedded.jetty.JettyReactiveWebServerFactory; -import org.springframework.boot.web.embedded.tomcat.TomcatReactiveWebServerFactory; -import org.springframework.boot.web.embedded.undertow.UndertowReactiveWebServerFactory; import org.springframework.boot.web.reactive.server.ConfigurableReactiveWebServerFactory; import org.springframework.boot.web.server.WebServerFactoryCustomizer; -import org.springframework.context.EnvironmentAware; import org.springframework.core.Ordered; -import org.springframework.core.env.Environment; /** - * Default {@link WebServerFactoryCustomizer} for reactive servers. + * {@link WebServerFactoryCustomizer} to apply {@link ServerProperties} to reactive + * servers. * * @author Brian Clozel * @author Yunkun Huang * @since 2.0.0 */ -public class DefaultReactiveWebServerFactoryCustomizer - implements WebServerFactoryCustomizer, - EnvironmentAware, Ordered { +public class ReactiveWebServerFactoryCustomizer implements + WebServerFactoryCustomizer, Ordered { private final ServerProperties serverProperties; - private Environment environment; - - public DefaultReactiveWebServerFactoryCustomizer(ServerProperties serverProperties) { + public ReactiveWebServerFactoryCustomizer(ServerProperties serverProperties) { this.serverProperties = serverProperties; } @@ -54,11 +44,6 @@ public class DefaultReactiveWebServerFactoryCustomizer return 0; } - @Override - public void setEnvironment(Environment environment) { - this.environment = environment; - } - @Override public void customize(ConfigurableReactiveWebServerFactory factory) { PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); @@ -67,15 +52,6 @@ public class DefaultReactiveWebServerFactoryCustomizer map.from(this.serverProperties::getSsl).to(factory::setSsl); map.from(this.serverProperties::getCompression).to(factory::setCompression); map.from(this.serverProperties::getHttp2).to(factory::setHttp2); - map.from(() -> factory).whenInstanceOf(TomcatReactiveWebServerFactory.class).to( - (tomcatFactory) -> TomcatCustomizer.customizeTomcat(this.serverProperties, - this.environment, tomcatFactory)); - map.from(() -> factory).whenInstanceOf(JettyReactiveWebServerFactory.class).to( - (jettyFactory) -> JettyCustomizer.customizeJetty(this.serverProperties, - this.environment, jettyFactory)); - map.from(() -> factory).whenInstanceOf(UndertowReactiveWebServerFactory.class) - .to((undertowFactory) -> UndertowCustomizer.customizeUndertow( - this.serverProperties, this.environment, undertowFactory)); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DefaultServletWebServerFactoryCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DefaultServletWebServerFactoryCustomizer.java deleted file mode 100644 index 7c174efc8ba..00000000000 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DefaultServletWebServerFactoryCustomizer.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2012-2018 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.servlet; - -import org.springframework.boot.autoconfigure.web.ServerProperties; -import org.springframework.boot.autoconfigure.web.embedded.jetty.JettyCustomizer; -import org.springframework.boot.autoconfigure.web.embedded.tomcat.TomcatCustomizer; -import org.springframework.boot.autoconfigure.web.embedded.undertow.UndertowCustomizer; -import org.springframework.boot.context.properties.PropertyMapper; -import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory; -import org.springframework.boot.web.embedded.tomcat.ConfigurableTomcatWebServerFactory; -import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; -import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory; -import org.springframework.boot.web.server.WebServerFactoryCustomizer; -import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; -import org.springframework.context.EnvironmentAware; -import org.springframework.core.Ordered; -import org.springframework.core.env.Environment; -import org.springframework.util.ObjectUtils; - -/** - * Default {@link WebServerFactoryCustomizer} for {@link ServerProperties}. - * - * @author Brian Clozel - * @author Stephane Nicoll - * @author Olivier Lamy - * @author Yunkun Huang - * @since 2.0.0 - */ -public class DefaultServletWebServerFactoryCustomizer - implements WebServerFactoryCustomizer, - EnvironmentAware, Ordered { - - private final ServerProperties serverProperties; - - private Environment environment; - - public DefaultServletWebServerFactoryCustomizer(ServerProperties serverProperties) { - this.serverProperties = serverProperties; - } - - public void setLoader(String value) { - // no op to support Tomcat running as a traditional server (not embedded) - } - - @Override - public int getOrder() { - return 0; - } - - @Override - public void setEnvironment(Environment environment) { - this.environment = environment; - } - - @Override - public void customize(ConfigurableServletWebServerFactory factory) { - PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); - map.from(this.serverProperties::getPort).to(factory::setPort); - map.from(this.serverProperties::getAddress).to(factory::setAddress); - map.from(this.serverProperties.getServlet()::getContextPath) - .to(factory::setContextPath); - map.from(this.serverProperties.getServlet()::getApplicationDisplayName) - .to(factory::setDisplayName); - map.from(this.serverProperties.getServlet()::getSession).to(factory::setSession); - map.from(this.serverProperties::getSsl).to(factory::setSsl); - map.from(this.serverProperties::getServlet).as(ServerProperties.Servlet::getJsp) - .to(factory::setJsp); - map.from(this.serverProperties::getCompression).to(factory::setCompression); - map.from(this.serverProperties::getHttp2).to(factory::setHttp2); - map.from(this.serverProperties::getServerHeader).to(factory::setServerHeader); - map.from(() -> factory).whenInstanceOf(TomcatServletWebServerFactory.class) - .to((tomcatFactory) -> { - TomcatCustomizer.customizeTomcat(this.serverProperties, - this.environment, tomcatFactory); - TomcatServletCustomizer.customizeTomcat(this.serverProperties, - this.environment, tomcatFactory); - }); - map.from(() -> factory).whenInstanceOf(JettyServletWebServerFactory.class).to( - (jettyFactory) -> JettyCustomizer.customizeJetty(this.serverProperties, - this.environment, jettyFactory)); - map.from(() -> factory).whenInstanceOf(UndertowServletWebServerFactory.class) - .to((undertowFactory) -> UndertowCustomizer.customizeUndertow( - this.serverProperties, this.environment, undertowFactory)); - map.from(this.serverProperties.getServlet()::getContextParameters) - .to(factory::setInitParameters); - } - - private static class TomcatServletCustomizer { - - public static void customizeTomcat(ServerProperties serverProperties, - Environment environment, TomcatServletWebServerFactory factory) { - ServerProperties.Tomcat tomcatProperties = serverProperties.getTomcat(); - if (!ObjectUtils.isEmpty(tomcatProperties.getAdditionalTldSkipPatterns())) { - factory.getTldSkipPatterns() - .addAll(tomcatProperties.getAdditionalTldSkipPatterns()); - } - if (tomcatProperties.getRedirectContextRoot() != null) { - customizeRedirectContextRoot(factory, - tomcatProperties.getRedirectContextRoot()); - } - if (tomcatProperties.getUseRelativeRedirects() != null) { - customizeUseRelativeRedirects(factory, - tomcatProperties.getUseRelativeRedirects()); - } - } - - private static void customizeRedirectContextRoot( - ConfigurableTomcatWebServerFactory factory, boolean redirectContextRoot) { - factory.addContextCustomizers((context) -> context - .setMapperContextRootRedirectEnabled(redirectContextRoot)); - } - - private static void customizeUseRelativeRedirects( - ConfigurableTomcatWebServerFactory factory, - boolean useRelativeRedirects) { - factory.addContextCustomizers( - (context) -> context.setUseRelativeRedirects(useRelativeRedirects)); - } - } - -} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfiguration.java index ec363d220bb..fe45ace0c88 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -16,17 +16,8 @@ package org.springframework.boot.autoconfigure.web.servlet; -import javax.servlet.Servlet; import javax.servlet.ServletRequest; -import io.undertow.Undertow; -import org.apache.catalina.startup.Tomcat; -import org.apache.coyote.UpgradeProtocol; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.util.Loader; -import org.eclipse.jetty.webapp.WebAppContext; -import org.xnio.SslClientAuthMode; - import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; @@ -36,19 +27,12 @@ import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.boot.autoconfigure.AutoConfigureOrder; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type; -import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.boot.autoconfigure.web.ServerProperties; -import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar; import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory; -import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; -import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory; import org.springframework.boot.web.server.ErrorPageRegistrarBeanPostProcessor; import org.springframework.boot.web.server.WebServerFactoryCustomizerBeanPostProcessor; -import org.springframework.boot.web.servlet.server.ServletWebServerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -66,65 +50,28 @@ import org.springframework.util.ObjectUtils; * @author Brian Clozel * @author Stephane Nicoll */ -@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @Configuration +@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @ConditionalOnClass(ServletRequest.class) @ConditionalOnWebApplication(type = Type.SERVLET) @EnableConfigurationProperties(ServerProperties.class) -@Import(BeanPostProcessorsRegistrar.class) +@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, + ServletWebServerFactoryConfiguration.EmbeddedTomcat.class, + ServletWebServerFactoryConfiguration.EmbeddedJetty.class, + ServletWebServerFactoryConfiguration.EmbeddedUndertow.class }) public class ServletWebServerFactoryAutoConfiguration { @Bean - @ConditionalOnMissingBean - public DefaultServletWebServerFactoryCustomizer serverPropertiesWebServerFactoryCustomizer( + public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer( ServerProperties serverProperties) { - return new DefaultServletWebServerFactoryCustomizer(serverProperties); + return new ServletWebServerFactoryCustomizer(serverProperties); } - /** - * Nested configuration if Tomcat is being used. - */ - @Configuration - @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class }) - @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) - public static class EmbeddedTomcat { - - @Bean - public TomcatServletWebServerFactory tomcatServletWebServerFactory() { - return new TomcatServletWebServerFactory(); - } - - } - - /** - * Nested configuration if Jetty is being used. - */ - @Configuration - @ConditionalOnClass({ Servlet.class, Server.class, Loader.class, - WebAppContext.class }) - @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) - public static class EmbeddedJetty { - - @Bean - public JettyServletWebServerFactory JettyServletWebServerFactory() { - return new JettyServletWebServerFactory(); - } - - } - - /** - * Nested configuration if Undertow is being used. - */ - @Configuration - @ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class }) - @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) - public static class EmbeddedUndertow { - - @Bean - public UndertowServletWebServerFactory undertowServletWebServerFactory() { - return new UndertowServletWebServerFactory(); - } - + @Bean + @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat") + public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer( + ServerProperties serverProperties) { + return new TomcatServletWebServerFactoryCustomizer(serverProperties); } /** diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryConfiguration.java new file mode 100644 index 00000000000..02761efa74c --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryConfiguration.java @@ -0,0 +1,96 @@ +/* + * Copyright 2012-2018 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.servlet; + +import javax.servlet.Servlet; + +import io.undertow.Undertow; +import org.apache.catalina.startup.Tomcat; +import org.apache.coyote.UpgradeProtocol; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.Loader; +import org.eclipse.jetty.webapp.WebAppContext; +import org.xnio.SslClientAuthMode; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.SearchStrategy; +import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory; +import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; +import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory; +import org.springframework.boot.web.servlet.server.ServletWebServerFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Configuration classes for servlet web servers + *

+ * Those should be {@code @Import} in a regular auto-configuration class to guarantee + * their order of execution. + * + * @author Phillip Webb + * @author Dave Syer + * @author Ivan Sopov + * @author Brian Clozel + * @author Stephane Nicoll + */ +class ServletWebServerFactoryConfiguration { + + @Configuration + @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class }) + @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) + public static class EmbeddedTomcat { + + @Bean + public TomcatServletWebServerFactory tomcatServletWebServerFactory() { + return new TomcatServletWebServerFactory(); + } + + } + + /** + * Nested configuration if Jetty is being used. + */ + @Configuration + @ConditionalOnClass({ Servlet.class, Server.class, Loader.class, + WebAppContext.class }) + @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) + public static class EmbeddedJetty { + + @Bean + public JettyServletWebServerFactory JettyServletWebServerFactory() { + return new JettyServletWebServerFactory(); + } + + } + + /** + * Nested configuration if Undertow is being used. + */ + @Configuration + @ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class }) + @ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT) + public static class EmbeddedUndertow { + + @Bean + public UndertowServletWebServerFactory undertowServletWebServerFactory() { + return new UndertowServletWebServerFactory(); + } + + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryCustomizer.java new file mode 100644 index 00000000000..0ccef444fe7 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryCustomizer.java @@ -0,0 +1,69 @@ +/* + * Copyright 2012-2018 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.servlet; + +import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.boot.context.properties.PropertyMapper; +import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; +import org.springframework.core.Ordered; + +/** + * {@link WebServerFactoryCustomizer} to apply {@link ServerProperties} to servlet web + * servers. + * + * @author Brian Clozel + * @author Stephane Nicoll + * @author Olivier Lamy + * @author Yunkun Huang + * @since 2.0.0 + */ +public class ServletWebServerFactoryCustomizer implements + WebServerFactoryCustomizer, Ordered { + + private final ServerProperties serverProperties; + + public ServletWebServerFactoryCustomizer(ServerProperties serverProperties) { + this.serverProperties = serverProperties; + } + + @Override + public int getOrder() { + return 0; + } + + @Override + public void customize(ConfigurableServletWebServerFactory factory) { + PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); + map.from(this.serverProperties::getPort).to(factory::setPort); + map.from(this.serverProperties::getAddress).to(factory::setAddress); + map.from(this.serverProperties.getServlet()::getContextPath) + .to(factory::setContextPath); + map.from(this.serverProperties.getServlet()::getApplicationDisplayName) + .to(factory::setDisplayName); + map.from(this.serverProperties.getServlet()::getSession).to(factory::setSession); + map.from(this.serverProperties::getSsl).to(factory::setSsl); + map.from(this.serverProperties::getServlet).as(ServerProperties.Servlet::getJsp) + .to(factory::setJsp); + map.from(this.serverProperties::getCompression).to(factory::setCompression); + map.from(this.serverProperties::getHttp2).to(factory::setHttp2); + map.from(this.serverProperties::getServerHeader).to(factory::setServerHeader); + map.from(this.serverProperties.getServlet()::getContextParameters) + .to(factory::setInitParameters); + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/TomcatServletWebServerFactoryCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/TomcatServletWebServerFactoryCustomizer.java new file mode 100644 index 00000000000..ce1ef77dbe4 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/TomcatServletWebServerFactoryCustomizer.java @@ -0,0 +1,77 @@ +/* + * Copyright 2012-2018 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.servlet; + +import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.boot.web.embedded.tomcat.ConfigurableTomcatWebServerFactory; +import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; +import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.core.Ordered; +import org.springframework.util.ObjectUtils; + +/** + * {@link WebServerFactoryCustomizer} to apply {@link ServerProperties} to Tomcat web + * servers. + * + * @author Brian Clozel + * @author Phillip Webb + * @since 2.0.0 + */ +public class TomcatServletWebServerFactoryCustomizer + implements WebServerFactoryCustomizer, Ordered { + + private final ServerProperties serverProperties; + + public TomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) { + this.serverProperties = serverProperties; + } + + @Override + public int getOrder() { + return 0; + } + + @Override + public void customize(TomcatServletWebServerFactory factory) { + ServerProperties.Tomcat tomcatProperties = this.serverProperties.getTomcat(); + if (!ObjectUtils.isEmpty(tomcatProperties.getAdditionalTldSkipPatterns())) { + factory.getTldSkipPatterns() + .addAll(tomcatProperties.getAdditionalTldSkipPatterns()); + } + if (tomcatProperties.getRedirectContextRoot() != null) { + customizeRedirectContextRoot(factory, + tomcatProperties.getRedirectContextRoot()); + } + if (tomcatProperties.getUseRelativeRedirects() != null) { + customizeUseRelativeRedirects(factory, + tomcatProperties.getUseRelativeRedirects()); + } + } + + private void customizeRedirectContextRoot(ConfigurableTomcatWebServerFactory factory, + boolean redirectContextRoot) { + factory.addContextCustomizers((context) -> context + .setMapperContextRootRedirectEnabled(redirectContextRoot)); + } + + private void customizeUseRelativeRedirects(ConfigurableTomcatWebServerFactory factory, + boolean useRelativeRedirects) { + factory.addContextCustomizers( + (context) -> context.setUseRelativeRedirects(useRelativeRedirects)); + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcRegistrations.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcRegistrations.java index 87349e8343d..4a4a958e44f 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcRegistrations.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcRegistrations.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2017 the original author or authors. + * Copyright 2012-2018 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. @@ -30,7 +30,7 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl * to choose from redundant MVC components. * * @author Brian Clozel - * @since 1.4.0 + * @since 2.0.0 * @see org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.EnableWebMvcConfiguration */ public interface WebMvcRegistrations { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcRegistrationsAdapter.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcRegistrationsAdapter.java deleted file mode 100644 index ae7eb7fd09d..00000000000 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/WebMvcRegistrationsAdapter.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2012-2017 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.servlet; - -/** - * An implementation of {@link WebMvcRegistrations} with empty methods allowing - * sub-classes to override only the methods they're interested in. - * - * @author Brian Clozel - * @since 1.4.0 - * @deprecated as of 2.0.0 {@link WebMvcRegistrations} has default methods (made possible - * by a Java 8 baseline) and can be implemented directly without the need for this adapter - */ -@Deprecated -public class WebMvcRegistrationsAdapter implements WebMvcRegistrations { - -} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories index 6844cadf9c9..b140c1f7501 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories @@ -110,6 +110,7 @@ org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration, org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\ org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\ org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\ +org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\ org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\ diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizerTests.java new file mode 100644 index 00000000000..3c8970001ae --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/JettyWebServerFactoryCustomizerTests.java @@ -0,0 +1,160 @@ +/* + * Copyright 2012-2018 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.embedded; + +import java.io.File; +import java.io.IOException; +import java.util.Locale; +import java.util.TimeZone; + +import org.eclipse.jetty.server.NCSARequestLog; +import org.eclipse.jetty.server.RequestLog; +import org.junit.Before; +import org.junit.Test; + +import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.boot.context.properties.bind.Bindable; +import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.boot.context.properties.source.ConfigurationPropertySources; +import org.springframework.boot.web.embedded.jetty.ConfigurableJettyWebServerFactory; +import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory; +import org.springframework.boot.web.embedded.jetty.JettyWebServer; +import org.springframework.mock.env.MockEnvironment; +import org.springframework.test.context.support.TestPropertySourceUtils; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +/** + * Tests for {@link JettyWebServerFactoryCustomizer}. + * + * @author Brian Clozel + * @author Phillip Webb + */ +public class JettyWebServerFactoryCustomizerTests { + + private MockEnvironment environment; + + private ServerProperties serverProperties; + + private JettyWebServerFactoryCustomizer customizer; + + @Before + public void setup() { + this.environment = new MockEnvironment(); + this.serverProperties = new ServerProperties(); + ConfigurationPropertySources.attach(this.environment); + this.customizer = new JettyWebServerFactoryCustomizer(this.environment, + this.serverProperties); + } + + @Test + public void deduceUseForwardHeaders() { + this.environment.setProperty("DYNO", "-"); + ConfigurableJettyWebServerFactory factory = mock( + ConfigurableJettyWebServerFactory.class); + this.customizer.customize(factory); + verify(factory).setUseForwardHeaders(true); + } + + @Test + public void defaultUseForwardHeaders() { + ConfigurableJettyWebServerFactory factory = mock( + ConfigurableJettyWebServerFactory.class); + this.customizer.customize(factory); + verify(factory).setUseForwardHeaders(false); + } + + @Test + public void accessLogCanBeCustomized() throws IOException { + File logFile = File.createTempFile("jetty_log", ".log"); + String timezone = TimeZone.getDefault().getID(); + bind("server.jetty.accesslog.enabled=true", + "server.jetty.accesslog.filename=" + logFile.getAbsolutePath(), + "server.jetty.accesslog.file-date-format=yyyy-MM-dd", + "server.jetty.accesslog.retention-period=42", + "server.jetty.accesslog.append=true", + "server.jetty.accesslog.extended-format=true", + "server.jetty.accesslog.date-format=HH:mm:ss", + "server.jetty.accesslog.locale=en_BE", + "server.jetty.accesslog.time-zone=" + timezone, + "server.jetty.accesslog.log-cookies=true", + "server.jetty.accesslog.log-server=true", + "server.jetty.accesslog.log-latency=true"); + JettyWebServer server = customizeAndGetServer(); + NCSARequestLog requestLog = getNCSARequestLog(server); + assertThat(requestLog.getFilename()).isEqualTo(logFile.getAbsolutePath()); + assertThat(requestLog.getFilenameDateFormat()).isEqualTo("yyyy-MM-dd"); + assertThat(requestLog.getRetainDays()).isEqualTo(42); + assertThat(requestLog.isAppend()).isTrue(); + assertThat(requestLog.isExtended()).isTrue(); + assertThat(requestLog.getLogDateFormat()).isEqualTo("HH:mm:ss"); + assertThat(requestLog.getLogLocale()).isEqualTo(new Locale("en", "BE")); + assertThat(requestLog.getLogTimeZone()).isEqualTo(timezone); + assertThat(requestLog.getLogCookies()).isTrue(); + assertThat(requestLog.getLogServer()).isTrue(); + assertThat(requestLog.getLogLatency()).isTrue(); + } + + @Test + public void accessLogCanBeEnabled() { + bind("server.jetty.accesslog.enabled=true"); + JettyWebServer server = customizeAndGetServer(); + NCSARequestLog requestLog = getNCSARequestLog(server); + assertThat(requestLog.getFilename()).isNull(); + assertThat(requestLog.isAppend()).isFalse(); + assertThat(requestLog.isExtended()).isFalse(); + assertThat(requestLog.getLogCookies()).isFalse(); + assertThat(requestLog.getLogServer()).isFalse(); + assertThat(requestLog.getLogLatency()).isFalse(); + } + + private NCSARequestLog getNCSARequestLog(JettyWebServer server) { + RequestLog requestLog = server.getServer().getRequestLog(); + assertThat(requestLog).isInstanceOf(NCSARequestLog.class); + return (NCSARequestLog) requestLog; + } + + @Test + public void setUseForwardHeaders() { + this.serverProperties.setUseForwardHeaders(true); + ConfigurableJettyWebServerFactory factory = mock( + ConfigurableJettyWebServerFactory.class); + this.customizer.customize(factory); + verify(factory).setUseForwardHeaders(true); + } + + private void bind(String... inlinedProperties) { + TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.environment, + inlinedProperties); + new Binder(ConfigurationPropertySources.get(this.environment)).bind("server", + Bindable.ofInstance(this.serverProperties)); + } + + private JettyWebServer customizeAndGetServer() { + JettyServletWebServerFactory factory = customizeAndGetFactory(); + return (JettyWebServer) factory.getWebServer(); + } + + private JettyServletWebServerFactory customizeAndGetFactory() { + JettyServletWebServerFactory factory = new JettyServletWebServerFactory(0); + this.customizer.customize(factory); + return factory; + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizerTests.java new file mode 100644 index 00000000000..3cf616e88a8 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizerTests.java @@ -0,0 +1,285 @@ +/* + * Copyright 2012-2018 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.embedded; + +import java.util.function.Consumer; + +import org.apache.catalina.Context; +import org.apache.catalina.Valve; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.valves.AccessLogValve; +import org.apache.catalina.valves.ErrorReportValve; +import org.apache.catalina.valves.RemoteIpValve; +import org.apache.coyote.AbstractProtocol; +import org.junit.Before; +import org.junit.Test; + +import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.boot.context.properties.bind.Bindable; +import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.boot.context.properties.source.ConfigurationPropertySources; +import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; +import org.springframework.boot.web.embedded.tomcat.TomcatWebServer; +import org.springframework.mock.env.MockEnvironment; +import org.springframework.test.context.support.TestPropertySourceUtils; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link TomcatWebServerFactoryCustomizer} + * + * @author Brian Clozel + * @author Phillip Webb + */ +public class TomcatWebServerFactoryCustomizerTests { + + private MockEnvironment environment; + + private ServerProperties serverProperties; + + private TomcatWebServerFactoryCustomizer customizer; + + @Before + public void setup() { + this.environment = new MockEnvironment(); + this.serverProperties = new ServerProperties(); + ConfigurationPropertySources.attach(this.environment); + this.customizer = new TomcatWebServerFactoryCustomizer(this.environment, + this.serverProperties); + } + + @Test + public void customAcceptCount() { + bind("server.tomcat.accept-count=10"); + customizeAndRunServer((server) -> assertThat(((AbstractProtocol) server + .getTomcat().getConnector().getProtocolHandler()).getAcceptCount()) + .isEqualTo(10)); + } + + @Test + public void customBackgroundProcessorDelay() { + bind("server.tomcat.background-processor-delay=5"); + TomcatWebServer server = customizeAndGetServer(); + assertThat(server.getTomcat().getEngine().getBackgroundProcessorDelay()) + .isEqualTo(5); + } + + @Test + public void customDisableMaxHttpPostSize() { + bind("server.tomcat.max-http-post-size=-1"); + customizeAndRunServer( + (server) -> assertThat(server.getTomcat().getConnector().getMaxPostSize()) + .isEqualTo(-1)); + } + + @Test + public void customMaxConnections() { + bind("server.tomcat.max-connections=5"); + customizeAndRunServer((server) -> assertThat(((AbstractProtocol) server + .getTomcat().getConnector().getProtocolHandler()).getMaxConnections()) + .isEqualTo(5)); + } + + @Test + public void customMaxHttpPostSize() { + bind("server.tomcat.max-http-post-size=10000"); + customizeAndRunServer( + (server) -> assertThat(server.getTomcat().getConnector().getMaxPostSize()) + .isEqualTo(10000)); + } + + @Test + public void customRemoteIpValve() { + bind("server.tomcat.remote-ip-header=x-my-remote-ip-header", + "server.tomcat.protocol-header=x-my-protocol-header", + "server.tomcat.internal-proxies=192.168.0.1", + "server.tomcat.port-header=x-my-forward-port", + "server.tomcat.protocol-header-https-value=On"); + TomcatServletWebServerFactory factory = customizeAndGetFactory(); + assertThat(factory.getEngineValves()).hasSize(1); + Valve valve = factory.getEngineValves().iterator().next(); + assertThat(valve).isInstanceOf(RemoteIpValve.class); + RemoteIpValve remoteIpValve = (RemoteIpValve) valve; + assertThat(remoteIpValve.getProtocolHeader()).isEqualTo("x-my-protocol-header"); + assertThat(remoteIpValve.getProtocolHeaderHttpsValue()).isEqualTo("On"); + assertThat(remoteIpValve.getRemoteIpHeader()).isEqualTo("x-my-remote-ip-header"); + assertThat(remoteIpValve.getPortHeader()).isEqualTo("x-my-forward-port"); + assertThat(remoteIpValve.getInternalProxies()).isEqualTo("192.168.0.1"); + } + + @Test + public void customStaticResourceCacheTtl() { + bind("server.tomcat.resource.cache-ttl=10000"); + customizeAndRunServer((server) -> { + Tomcat tomcat = server.getTomcat(); + Context context = (Context) tomcat.getHost().findChildren()[0]; + assertThat(context.getResources().getCacheTtl()).isEqualTo(10000L); + }); + } + + @Test + public void deduceUseForwardHeaders() { + this.environment.setProperty("DYNO", "-"); + testRemoteIpValveConfigured(); + } + + @Test + public void defaultRemoteIpValve() { + // Since 1.1.7 you need to specify at least the protocol + bind("server.tomcat.protocol-header=X-Forwarded-Proto", + "server.tomcat.remote-ip-header=X-Forwarded-For"); + testRemoteIpValveConfigured(); + } + + @Test + public void setUseForwardHeaders() { + // Since 1.3.0 no need to explicitly set header names if use-forward-header=true + this.serverProperties.setUseForwardHeaders(true); + testRemoteIpValveConfigured(); + } + + private void testRemoteIpValveConfigured() { + TomcatServletWebServerFactory factory = customizeAndGetFactory(); + assertThat(factory.getEngineValves()).hasSize(1); + Valve valve = factory.getEngineValves().iterator().next(); + assertThat(valve).isInstanceOf(RemoteIpValve.class); + RemoteIpValve remoteIpValve = (RemoteIpValve) valve; + assertThat(remoteIpValve.getProtocolHeader()).isEqualTo("X-Forwarded-Proto"); + assertThat(remoteIpValve.getProtocolHeaderHttpsValue()).isEqualTo("https"); + assertThat(remoteIpValve.getRemoteIpHeader()).isEqualTo("X-Forwarded-For"); + String expectedInternalProxies = "10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" // 10/8 + + "192\\.168\\.\\d{1,3}\\.\\d{1,3}|" // 192.168/16 + + "169\\.254\\.\\d{1,3}\\.\\d{1,3}|" // 169.254/16 + + "127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" // 127/8 + + "172\\.1[6-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" // 172.16/12 + + "172\\.2[0-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" + + "172\\.3[0-1]{1}\\.\\d{1,3}\\.\\d{1,3}"; + assertThat(remoteIpValve.getInternalProxies()).isEqualTo(expectedInternalProxies); + } + + @Test + public void defaultBackgroundProcessorDelay() { + TomcatWebServer server = customizeAndGetServer(); + assertThat(server.getTomcat().getEngine().getBackgroundProcessorDelay()) + .isEqualTo(30); + } + + @Test + public void disableRemoteIpValve() { + bind("server.tomcat.remote-ip-header=", "server.tomcat.protocol-header="); + TomcatServletWebServerFactory factory = customizeAndGetFactory(); + assertThat(factory.getEngineValves()).isEmpty(); + } + + @Test + public void errorReportValveIsConfiguredToNotReportStackTraces() { + TomcatWebServer server = customizeAndGetServer(); + Valve[] valves = server.getTomcat().getHost().getPipeline().getValves(); + assertThat(valves).hasAtLeastOneElementOfType(ErrorReportValve.class); + for (Valve valve : valves) { + if (valve instanceof ErrorReportValve) { + ErrorReportValve errorReportValve = (ErrorReportValve) valve; + assertThat(errorReportValve.isShowReport()).isFalse(); + assertThat(errorReportValve.isShowServerInfo()).isFalse(); + } + } + } + + @Test + public void testCustomizeMinSpareThreads() { + bind("server.tomcat.min-spare-threads=10"); + assertThat(this.serverProperties.getTomcat().getMinSpareThreads()).isEqualTo(10); + } + + @Test + public void accessLogBufferingCanBeDisabled() { + bind("server.tomcat.accesslog.enabled=true", + "server.tomcat.accesslog.buffered=false"); + TomcatServletWebServerFactory factory = customizeAndGetFactory(); + assertThat(((AccessLogValve) factory.getEngineValves().iterator().next()) + .isBuffered()).isFalse(); + } + + @Test + public void accessLogCanBeEnabled() { + bind("server.tomcat.accesslog.enabled=true"); + TomcatServletWebServerFactory factory = customizeAndGetFactory(); + assertThat(factory.getEngineValves()).hasSize(1); + assertThat(factory.getEngineValves()).first().isInstanceOf(AccessLogValve.class); + } + + @Test + public void accessLogFileDateFormatByDefault() { + bind("server.tomcat.accesslog.enabled=true"); + TomcatServletWebServerFactory factory = customizeAndGetFactory(); + assertThat(((AccessLogValve) factory.getEngineValves().iterator().next()) + .getFileDateFormat()).isEqualTo(".yyyy-MM-dd"); + } + + @Test + public void accessLogFileDateFormatCanBeRedefined() { + bind("server.tomcat.accesslog.enabled=true", + "server.tomcat.accesslog.file-date-format=yyyy-MM-dd.HH"); + TomcatServletWebServerFactory factory = customizeAndGetFactory(); + assertThat(((AccessLogValve) factory.getEngineValves().iterator().next()) + .getFileDateFormat()).isEqualTo("yyyy-MM-dd.HH"); + } + + @Test + public void accessLogIsBufferedByDefault() { + bind("server.tomcat.accesslog.enabled=true"); + TomcatServletWebServerFactory factory = customizeAndGetFactory(); + assertThat(((AccessLogValve) factory.getEngineValves().iterator().next()) + .isBuffered()).isTrue(); + } + + @Test + public void accessLogIsDisabledByDefault() { + TomcatServletWebServerFactory factory = customizeAndGetFactory(); + assertThat(factory.getEngineValves()).isEmpty(); + } + + private void bind(String... inlinedProperties) { + TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.environment, + inlinedProperties); + new Binder(ConfigurationPropertySources.get(this.environment)).bind("server", + Bindable.ofInstance(this.serverProperties)); + } + + private void customizeAndRunServer(Consumer consumer) { + TomcatWebServer server = customizeAndGetServer(); + server.start(); + try { + consumer.accept(server); + } + finally { + server.stop(); + } + } + + private TomcatWebServer customizeAndGetServer() { + TomcatServletWebServerFactory factory = customizeAndGetFactory(); + return (TomcatWebServer) factory.getWebServer(); + } + + private TomcatServletWebServerFactory customizeAndGetFactory() { + TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(0); + this.customizer.customize(factory); + return factory; + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/UndertowWebServerFactoryCustomizerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/UndertowWebServerFactoryCustomizerTests.java new file mode 100644 index 00000000000..5146117d8e2 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/UndertowWebServerFactoryCustomizerTests.java @@ -0,0 +1,120 @@ +/* + * Copyright 2012-2018 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.embedded; + +import java.io.File; + +import org.junit.Before; +import org.junit.Test; + +import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.boot.context.properties.bind.Bindable; +import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.boot.context.properties.source.ConfigurationPropertySources; +import org.springframework.boot.web.embedded.undertow.ConfigurableUndertowWebServerFactory; +import org.springframework.mock.env.MockEnvironment; +import org.springframework.test.context.support.TestPropertySourceUtils; + +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +/** + * Tests for {@link UndertowWebServerFactoryCustomizer}. + * + * @author Brian Clozel + * @author Phillip Webb + */ +public class UndertowWebServerFactoryCustomizerTests { + + private MockEnvironment environment; + + private ServerProperties serverProperties; + + private UndertowWebServerFactoryCustomizer customizer; + + @Before + public void setup() { + this.environment = new MockEnvironment(); + this.serverProperties = new ServerProperties(); + ConfigurationPropertySources.attach(this.environment); + this.customizer = new UndertowWebServerFactoryCustomizer(this.environment, + this.serverProperties); + } + + @Test + public void customizeUndertowAccessLog() { + bind("server.undertow.accesslog.enabled=true", + "server.undertow.accesslog.pattern=foo", + "server.undertow.accesslog.prefix=test_log", + "server.undertow.accesslog.suffix=txt", + "server.undertow.accesslog.dir=test-logs", + "server.undertow.accesslog.rotate=false"); + ConfigurableUndertowWebServerFactory factory = mock( + ConfigurableUndertowWebServerFactory.class); + this.customizer.customize(factory); + verify(factory).setAccessLogEnabled(true); + verify(factory).setAccessLogPattern("foo"); + verify(factory).setAccessLogPrefix("test_log"); + verify(factory).setAccessLogSuffix("txt"); + verify(factory).setAccessLogDirectory(new File("test-logs")); + verify(factory).setAccessLogRotate(false); + } + + @Test + public void deduceUseForwardHeadersUndertow() { + this.environment.setProperty("DYNO", "-"); + ConfigurableUndertowWebServerFactory factory = mock( + ConfigurableUndertowWebServerFactory.class); + this.customizer.customize(factory); + verify(factory).setUseForwardHeaders(true); + } + + @Test + public void defaultUseForwardHeadersUndertow() { + ConfigurableUndertowWebServerFactory factory = mock( + ConfigurableUndertowWebServerFactory.class); + this.customizer.customize(factory); + verify(factory).setUseForwardHeaders(false); + } + + @Test + public void setUseForwardHeadersUndertow() { + this.serverProperties.setUseForwardHeaders(true); + ConfigurableUndertowWebServerFactory factory = mock( + ConfigurableUndertowWebServerFactory.class); + this.customizer.customize(factory); + verify(factory).setUseForwardHeaders(true); + } + + @Test + public void skipNullElementsForUndertow() { + ConfigurableUndertowWebServerFactory factory = mock( + ConfigurableUndertowWebServerFactory.class); + this.customizer.customize(factory); + verify(factory, never()).setAccessLogEnabled(anyBoolean()); + } + + private void bind(String... inlinedProperties) { + TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.environment, + inlinedProperties); + new Binder(ConfigurationPropertySources.get(this.environment)).bind("server", + Bindable.ofInstance(this.serverProperties)); + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/DefaultReactiveWebServerFactoryCustomizerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/DefaultReactiveWebServerFactoryCustomizerTests.java deleted file mode 100644 index 7980e6f4765..00000000000 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/DefaultReactiveWebServerFactoryCustomizerTests.java +++ /dev/null @@ -1,538 +0,0 @@ -/* - * Copyright 2012-2018 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.reactive; - -import java.io.File; -import java.io.IOException; -import java.net.InetAddress; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -import java.util.TimeZone; - -import org.apache.catalina.Context; -import org.apache.catalina.Valve; -import org.apache.catalina.startup.Tomcat; -import org.apache.catalina.valves.AccessLogValve; -import org.apache.catalina.valves.ErrorReportValve; -import org.apache.catalina.valves.RemoteIpValve; -import org.apache.coyote.AbstractProtocol; -import org.eclipse.jetty.server.NCSARequestLog; -import org.eclipse.jetty.server.RequestLog; -import org.junit.Before; -import org.junit.Test; - -import org.springframework.boot.autoconfigure.web.ServerProperties; -import org.springframework.boot.context.properties.bind.Bindable; -import org.springframework.boot.context.properties.bind.Binder; -import org.springframework.boot.context.properties.source.ConfigurationPropertySource; -import org.springframework.boot.context.properties.source.MapConfigurationPropertySource; -import org.springframework.boot.web.embedded.jetty.JettyReactiveWebServerFactory; -import org.springframework.boot.web.embedded.jetty.JettyWebServer; -import org.springframework.boot.web.embedded.tomcat.TomcatReactiveWebServerFactory; -import org.springframework.boot.web.embedded.tomcat.TomcatWebServer; -import org.springframework.boot.web.embedded.undertow.UndertowReactiveWebServerFactory; -import org.springframework.boot.web.reactive.server.ConfigurableReactiveWebServerFactory; -import org.springframework.boot.web.server.Ssl; -import org.springframework.http.server.reactive.HttpHandler; -import org.springframework.mock.env.MockEnvironment; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; - -/** - * Tests for {@link DefaultReactiveWebServerFactoryCustomizer}. - * - * @author Brian Clozel - * @author Yunkun Huang - */ -public class DefaultReactiveWebServerFactoryCustomizerTests { - - private final ServerProperties properties = new ServerProperties(); - - private DefaultReactiveWebServerFactoryCustomizer customizer; - - @Before - public void setup() { - this.customizer = new DefaultReactiveWebServerFactoryCustomizer(this.properties); - } - - @Test - public void testCustomizeServerPort() { - ConfigurableReactiveWebServerFactory factory = mock( - ConfigurableReactiveWebServerFactory.class); - this.properties.setPort(9000); - this.customizer.customize(factory); - verify(factory).setPort(9000); - } - - @Test - public void testCustomizeServerAddress() { - ConfigurableReactiveWebServerFactory factory = mock( - ConfigurableReactiveWebServerFactory.class); - InetAddress address = mock(InetAddress.class); - this.properties.setAddress(address); - this.customizer.customize(factory); - verify(factory).setAddress(address); - } - - @Test - public void testCustomizeServerSsl() { - ConfigurableReactiveWebServerFactory factory = mock( - ConfigurableReactiveWebServerFactory.class); - Ssl ssl = mock(Ssl.class); - this.properties.setSsl(ssl); - this.customizer.customize(factory); - verify(factory).setSsl(ssl); - } - - @Test - public void tomcatAccessLogIsDisabledByDefault() { - TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory(); - this.customizer.customize(factory); - assertThat(factory.getEngineValves()).isEmpty(); - } - - @Test - public void tomcatAccessLogCanBeEnabled() { - TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory(); - Map map = new HashMap<>(); - map.put("server.tomcat.accesslog.enabled", "true"); - bindProperties(map); - this.customizer.customize(factory); - assertThat(factory.getEngineValves()).hasSize(1); - assertThat(factory.getEngineValves()).first().isInstanceOf(AccessLogValve.class); - } - - @Test - public void tomcatAccessLogFileDateFormatByDefault() { - TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory(); - Map map = new HashMap<>(); - map.put("server.tomcat.accesslog.enabled", "true"); - bindProperties(map); - this.customizer.customize(factory); - assertThat(((AccessLogValve) factory.getEngineValves().iterator().next()) - .getFileDateFormat()).isEqualTo(".yyyy-MM-dd"); - } - - @Test - public void tomcatAccessLogFileDateFormatCanBeRedefined() { - TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory(); - Map map = new HashMap<>(); - map.put("server.tomcat.accesslog.enabled", "true"); - map.put("server.tomcat.accesslog.file-date-format", "yyyy-MM-dd.HH"); - bindProperties(map); - this.customizer.customize(factory); - assertThat(((AccessLogValve) factory.getEngineValves().iterator().next()) - .getFileDateFormat()).isEqualTo("yyyy-MM-dd.HH"); - } - - @Test - public void tomcatAccessLogIsBufferedByDefault() { - TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory(); - Map map = new HashMap<>(); - map.put("server.tomcat.accesslog.enabled", "true"); - bindProperties(map); - this.customizer.customize(factory); - assertThat(((AccessLogValve) factory.getEngineValves().iterator().next()) - .isBuffered()).isTrue(); - } - - @Test - public void tomcatAccessLogBufferingCanBeDisabled() { - TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory(); - Map map = new HashMap<>(); - map.put("server.tomcat.accesslog.enabled", "true"); - map.put("server.tomcat.accesslog.buffered", "false"); - bindProperties(map); - this.customizer.customize(factory); - assertThat(((AccessLogValve) factory.getEngineValves().iterator().next()) - .isBuffered()).isFalse(); - } - - @Test - public void disableTomcatRemoteIpValve() { - Map map = new HashMap<>(); - map.put("server.tomcat.remote-ip-header", ""); - map.put("server.tomcat.protocol-header", ""); - bindProperties(map); - TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory(); - this.customizer.customize(factory); - assertThat(factory.getEngineValves()).isEmpty(); - } - - @Test - public void defaultTomcatBackgroundProcessorDelay() { - TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory(); - this.customizer.customize(factory); - TomcatWebServer webServer = (TomcatWebServer) factory - .getWebServer(mock(HttpHandler.class)); - assertThat(webServer.getTomcat().getEngine().getBackgroundProcessorDelay()) - .isEqualTo(30); - webServer.stop(); - } - - @Test - public void customTomcatBackgroundProcessorDelay() { - Map map = new HashMap<>(); - map.put("server.tomcat.background-processor-delay", "5"); - bindProperties(map); - TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory(); - this.customizer.customize(factory); - TomcatWebServer webServer = (TomcatWebServer) factory - .getWebServer(mock(HttpHandler.class)); - assertThat(webServer.getTomcat().getEngine().getBackgroundProcessorDelay()) - .isEqualTo(5); - webServer.stop(); - } - - @Test - public void defaultTomcatRemoteIpValve() { - Map map = new HashMap<>(); - // Since 1.1.7 you need to specify at least the protocol - map.put("server.tomcat.protocol-header", "X-Forwarded-Proto"); - map.put("server.tomcat.remote-ip-header", "X-Forwarded-For"); - bindProperties(map); - testRemoteIpValveConfigured(); - } - - @Test - public void setUseForwardHeadersTomcat() { - // Since 1.3.0 no need to explicitly set header names if use-forward-header=true - this.properties.setUseForwardHeaders(true); - testRemoteIpValveConfigured(); - } - - @Test - public void deduceUseForwardHeadersTomcat() { - this.customizer.setEnvironment(new MockEnvironment().withProperty("DYNO", "-")); - testRemoteIpValveConfigured(); - } - - private void testRemoteIpValveConfigured() { - TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory(); - this.customizer.customize(factory); - assertThat(factory.getEngineValves()).hasSize(1); - Valve valve = factory.getEngineValves().iterator().next(); - assertThat(valve).isInstanceOf(RemoteIpValve.class); - RemoteIpValve remoteIpValve = (RemoteIpValve) valve; - assertThat(remoteIpValve.getProtocolHeader()).isEqualTo("X-Forwarded-Proto"); - assertThat(remoteIpValve.getProtocolHeaderHttpsValue()).isEqualTo("https"); - assertThat(remoteIpValve.getRemoteIpHeader()).isEqualTo("X-Forwarded-For"); - String expectedInternalProxies = "10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" // 10/8 - + "192\\.168\\.\\d{1,3}\\.\\d{1,3}|" // 192.168/16 - + "169\\.254\\.\\d{1,3}\\.\\d{1,3}|" // 169.254/16 - + "127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" // 127/8 - + "172\\.1[6-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" // 172.16/12 - + "172\\.2[0-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" - + "172\\.3[0-1]{1}\\.\\d{1,3}\\.\\d{1,3}"; - assertThat(remoteIpValve.getInternalProxies()).isEqualTo(expectedInternalProxies); - } - - @Test - public void customTomcatRemoteIpValve() { - Map map = new HashMap<>(); - map.put("server.tomcat.remote-ip-header", "x-my-remote-ip-header"); - map.put("server.tomcat.protocol-header", "x-my-protocol-header"); - map.put("server.tomcat.internal-proxies", "192.168.0.1"); - map.put("server.tomcat.port-header", "x-my-forward-port"); - map.put("server.tomcat.protocol-header-https-value", "On"); - bindProperties(map); - TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory(); - this.customizer.customize(factory); - assertThat(factory.getEngineValves()).hasSize(1); - Valve valve = factory.getEngineValves().iterator().next(); - assertThat(valve).isInstanceOf(RemoteIpValve.class); - RemoteIpValve remoteIpValve = (RemoteIpValve) valve; - assertThat(remoteIpValve.getProtocolHeader()).isEqualTo("x-my-protocol-header"); - assertThat(remoteIpValve.getProtocolHeaderHttpsValue()).isEqualTo("On"); - assertThat(remoteIpValve.getRemoteIpHeader()).isEqualTo("x-my-remote-ip-header"); - assertThat(remoteIpValve.getPortHeader()).isEqualTo("x-my-forward-port"); - assertThat(remoteIpValve.getInternalProxies()).isEqualTo("192.168.0.1"); - } - - @Test - public void customTomcatAcceptCount() { - Map map = new HashMap<>(); - map.put("server.tomcat.accept-count", "10"); - bindProperties(map); - TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory(0); - this.customizer.customize(factory); - TomcatWebServer server = (TomcatWebServer) factory - .getWebServer(mock(HttpHandler.class)); - server.start(); - try { - assertThat(((AbstractProtocol) server.getTomcat().getConnector() - .getProtocolHandler()).getAcceptCount()).isEqualTo(10); - } - finally { - server.stop(); - } - } - - @Test - public void customTomcatMaxConnections() { - Map map = new HashMap<>(); - map.put("server.tomcat.max-connections", "5"); - bindProperties(map); - TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory(0); - this.customizer.customize(factory); - TomcatWebServer server = (TomcatWebServer) factory - .getWebServer(mock(HttpHandler.class)); - server.start(); - try { - assertThat(((AbstractProtocol) server.getTomcat().getConnector() - .getProtocolHandler()).getMaxConnections()).isEqualTo(5); - } - finally { - server.stop(); - } - } - - @Test - public void customTomcatMaxHttpPostSize() { - Map map = new HashMap<>(); - map.put("server.tomcat.max-http-post-size", "10000"); - bindProperties(map); - TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory(0); - this.customizer.customize(factory); - TomcatWebServer server = (TomcatWebServer) factory - .getWebServer(mock(HttpHandler.class)); - server.start(); - try { - assertThat(server.getTomcat().getConnector().getMaxPostSize()) - .isEqualTo(10000); - } - finally { - server.stop(); - } - } - - @Test - public void customTomcatDisableMaxHttpPostSize() { - Map map = new HashMap<>(); - map.put("server.tomcat.max-http-post-size", "-1"); - bindProperties(map); - TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory(0); - this.customizer.customize(factory); - TomcatWebServer server = (TomcatWebServer) factory - .getWebServer(mock(HttpHandler.class)); - server.start(); - try { - assertThat(server.getTomcat().getConnector().getMaxPostSize()).isEqualTo(-1); - } - finally { - server.stop(); - } - } - - @Test - public void testCustomizeTomcatMinSpareThreads() { - Map map = new HashMap<>(); - map.put("server.tomcat.min-spare-threads", "10"); - bindProperties(map); - assertThat(this.properties.getTomcat().getMinSpareThreads()).isEqualTo(10); - } - - @Test - public void customTomcatStaticResourceCacheTtl() { - Map map = new HashMap<>(); - map.put("server.tomcat.resource.cache-ttl", "10000"); - bindProperties(map); - TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory(0); - this.customizer.customize(factory); - TomcatWebServer server = (TomcatWebServer) factory - .getWebServer(mock(HttpHandler.class)); - server.start(); - try { - Tomcat tomcat = server.getTomcat(); - Context context = (Context) tomcat.getHost().findChildren()[0]; - assertThat(context.getResources().getCacheTtl()).isEqualTo(10000L); - } - finally { - server.stop(); - } - } - - @Test - public void errorReportValveIsConfiguredToNotReportStackTraces() { - TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory(); - Map map = new HashMap(); - bindProperties(map); - this.customizer.customize(factory); - Valve[] valves = ((TomcatWebServer) factory.getWebServer(mock(HttpHandler.class))) - .getTomcat().getHost().getPipeline().getValves(); - assertThat(valves).hasAtLeastOneElementOfType(ErrorReportValve.class); - for (Valve valve : valves) { - if (valve instanceof ErrorReportValve) { - ErrorReportValve errorReportValve = (ErrorReportValve) valve; - assertThat(errorReportValve.isShowReport()).isFalse(); - assertThat(errorReportValve.isShowServerInfo()).isFalse(); - } - } - } - - @Test - public void defaultUseForwardHeadersJetty() { - JettyReactiveWebServerFactory factory = spy(new JettyReactiveWebServerFactory()); - this.customizer.customize(factory); - verify(factory).setUseForwardHeaders(false); - } - - @Test - public void setUseForwardHeadersJetty() { - this.properties.setUseForwardHeaders(true); - JettyReactiveWebServerFactory factory = spy(new JettyReactiveWebServerFactory()); - this.customizer.customize(factory); - verify(factory).setUseForwardHeaders(true); - } - - @Test - public void deduceUseForwardHeadersJetty() { - this.customizer.setEnvironment(new MockEnvironment().withProperty("DYNO", "-")); - JettyReactiveWebServerFactory factory = spy(new JettyReactiveWebServerFactory()); - this.customizer.customize(factory); - verify(factory).setUseForwardHeaders(true); - } - - @Test - public void jettyAccessLogCanBeEnabled() { - JettyReactiveWebServerFactory factory = new JettyReactiveWebServerFactory(0); - Map map = new HashMap<>(); - map.put("server.jetty.accesslog.enabled", "true"); - bindProperties(map); - this.customizer.customize(factory); - JettyWebServer webServer = (JettyWebServer) factory - .getWebServer(mock(HttpHandler.class)); - try { - NCSARequestLog requestLog = getNCSARequestLog(webServer); - assertThat(requestLog.getFilename()).isNull(); - assertThat(requestLog.isAppend()).isFalse(); - assertThat(requestLog.isExtended()).isFalse(); - assertThat(requestLog.getLogCookies()).isFalse(); - assertThat(requestLog.getLogServer()).isFalse(); - assertThat(requestLog.getLogLatency()).isFalse(); - } - finally { - webServer.stop(); - } - } - - @Test - public void jettyAccessLogCanBeCustomized() throws IOException { - File logFile = File.createTempFile("jetty_log", ".log"); - JettyReactiveWebServerFactory factory = new JettyReactiveWebServerFactory(0); - Map map = new HashMap<>(); - String timezone = TimeZone.getDefault().getID(); - map.put("server.jetty.accesslog.enabled", "true"); - map.put("server.jetty.accesslog.filename", logFile.getAbsolutePath()); - map.put("server.jetty.accesslog.file-date-format", "yyyy-MM-dd"); - map.put("server.jetty.accesslog.retention-period", "42"); - map.put("server.jetty.accesslog.append", "true"); - map.put("server.jetty.accesslog.extended-format", "true"); - map.put("server.jetty.accesslog.date-format", "HH:mm:ss"); - map.put("server.jetty.accesslog.locale", "en_BE"); - map.put("server.jetty.accesslog.time-zone", timezone); - map.put("server.jetty.accesslog.log-cookies", "true"); - map.put("server.jetty.accesslog.log-server", "true"); - map.put("server.jetty.accesslog.log-latency", "true"); - bindProperties(map); - this.customizer.customize(factory); - JettyWebServer webServer = (JettyWebServer) factory - .getWebServer(mock(HttpHandler.class)); - NCSARequestLog requestLog = getNCSARequestLog(webServer); - try { - assertThat(requestLog.getFilename()).isEqualTo(logFile.getAbsolutePath()); - assertThat(requestLog.getFilenameDateFormat()).isEqualTo("yyyy-MM-dd"); - assertThat(requestLog.getRetainDays()).isEqualTo(42); - assertThat(requestLog.isAppend()).isTrue(); - assertThat(requestLog.isExtended()).isTrue(); - assertThat(requestLog.getLogDateFormat()).isEqualTo("HH:mm:ss"); - assertThat(requestLog.getLogLocale()).isEqualTo(new Locale("en", "BE")); - assertThat(requestLog.getLogTimeZone()).isEqualTo(timezone); - assertThat(requestLog.getLogCookies()).isTrue(); - assertThat(requestLog.getLogServer()).isTrue(); - assertThat(requestLog.getLogLatency()).isTrue(); - } - finally { - webServer.stop(); - } - } - - @Test - public void customizeUndertowAccessLog() { - Map map = new HashMap<>(); - map.put("server.undertow.accesslog.enabled", "true"); - map.put("server.undertow.accesslog.pattern", "foo"); - map.put("server.undertow.accesslog.prefix", "test_log"); - map.put("server.undertow.accesslog.suffix", "txt"); - map.put("server.undertow.accesslog.dir", "test-logs"); - map.put("server.undertow.accesslog.rotate", "false"); - bindProperties(map); - UndertowReactiveWebServerFactory factory = spy( - new UndertowReactiveWebServerFactory()); - this.customizer.customize(factory); - verify(factory).setAccessLogEnabled(true); - verify(factory).setAccessLogPattern("foo"); - verify(factory).setAccessLogPrefix("test_log"); - verify(factory).setAccessLogSuffix("txt"); - verify(factory).setAccessLogDirectory(new File("test-logs")); - verify(factory).setAccessLogRotate(false); - } - - @Test - public void setUseForwardHeadersUndertow() { - this.properties.setUseForwardHeaders(true); - UndertowReactiveWebServerFactory factory = spy( - new UndertowReactiveWebServerFactory()); - this.customizer.customize(factory); - verify(factory).setUseForwardHeaders(true); - } - - @Test - public void deduceUseForwardHeadersUndertow() { - this.customizer.setEnvironment(new MockEnvironment().withProperty("DYNO", "-")); - UndertowReactiveWebServerFactory factory = spy( - new UndertowReactiveWebServerFactory()); - this.customizer.customize(factory); - verify(factory).setUseForwardHeaders(true); - } - - @Test - public void skipNullElementsForUndertow() { - UndertowReactiveWebServerFactory factory = mock( - UndertowReactiveWebServerFactory.class); - this.customizer.customize(factory); - verify(factory, never()).setAccessLogEnabled(anyBoolean()); - } - - private NCSARequestLog getNCSARequestLog(JettyWebServer webServer) { - RequestLog requestLog = webServer.getServer().getRequestLog(); - assertThat(requestLog).isInstanceOf(NCSARequestLog.class); - return (NCSARequestLog) requestLog; - } - - private void bindProperties(Map map) { - ConfigurationPropertySource source = new MapConfigurationPropertySource(map); - new Binder(source).bind("server", Bindable.ofInstance(this.properties)); - } - -} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryAutoConfigurationTests.java index 758247a4bdc..d535b1cd0d5 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryAutoConfigurationTests.java @@ -55,7 +55,7 @@ public class ReactiveWebServerFactoryAutoConfigurationTests { assertThat(this.context.getBeansOfType(WebServerFactoryCustomizer.class)) .hasSize(1); assertThat(this.context - .getBeansOfType(DefaultReactiveWebServerFactoryCustomizer.class)) + .getBeansOfType(ReactiveWebServerFactoryCustomizer.class)) .hasSize(1); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryCustomizerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryCustomizerTests.java new file mode 100644 index 00000000000..53bcdaedc80 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveWebServerFactoryCustomizerTests.java @@ -0,0 +1,78 @@ +/* + * Copyright 2012-2018 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.reactive; + +import java.net.InetAddress; + +import org.junit.Before; +import org.junit.Test; + +import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.boot.web.reactive.server.ConfigurableReactiveWebServerFactory; +import org.springframework.boot.web.server.Ssl; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +/** + * Tests for {@link ReactiveWebServerFactoryCustomizer}. + * + * @author Brian Clozel + * @author Yunkun Huang + */ +public class ReactiveWebServerFactoryCustomizerTests { + + private ServerProperties properties = new ServerProperties(); + + private ReactiveWebServerFactoryCustomizer customizer; + + @Before + public void setup() { + this.customizer = new ReactiveWebServerFactoryCustomizer( + this.properties); + } + + @Test + public void testCustomizeServerPort() { + ConfigurableReactiveWebServerFactory factory = mock( + ConfigurableReactiveWebServerFactory.class); + this.properties.setPort(9000); + this.customizer.customize(factory); + verify(factory).setPort(9000); + } + + @Test + public void testCustomizeServerAddress() { + ConfigurableReactiveWebServerFactory factory = mock( + ConfigurableReactiveWebServerFactory.class); + InetAddress address = mock(InetAddress.class); + this.properties.setAddress(address); + this.customizer.customize(factory); + verify(factory).setAddress(address); + } + + @Test + public void testCustomizeServerSsl() { + ConfigurableReactiveWebServerFactory factory = mock( + ConfigurableReactiveWebServerFactory.class); + Ssl ssl = mock(Ssl.class); + this.properties.setSsl(ssl); + this.customizer.customize(factory); + verify(factory).setSsl(ssl); + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/DefaultServletWebServerFactoryCustomizerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/DefaultServletWebServerFactoryCustomizerTests.java deleted file mode 100644 index e54f1b10d58..00000000000 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/DefaultServletWebServerFactoryCustomizerTests.java +++ /dev/null @@ -1,677 +0,0 @@ -/* - * Copyright 2012-2018 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.servlet; - -import java.io.File; -import java.io.IOException; -import java.time.Duration; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; -import java.util.TimeZone; - -import org.apache.catalina.Context; -import org.apache.catalina.Valve; -import org.apache.catalina.startup.Tomcat; -import org.apache.catalina.valves.AccessLogValve; -import org.apache.catalina.valves.ErrorReportValve; -import org.apache.catalina.valves.RemoteIpValve; -import org.apache.coyote.AbstractProtocol; -import org.eclipse.jetty.server.NCSARequestLog; -import org.eclipse.jetty.server.RequestLog; -import org.junit.Before; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.MockitoAnnotations; - -import org.springframework.boot.autoconfigure.web.ServerProperties; -import org.springframework.boot.context.properties.bind.Bindable; -import org.springframework.boot.context.properties.bind.Binder; -import org.springframework.boot.context.properties.source.ConfigurationPropertySource; -import org.springframework.boot.context.properties.source.MapConfigurationPropertySource; -import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory; -import org.springframework.boot.web.embedded.jetty.JettyWebServer; -import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; -import org.springframework.boot.web.embedded.tomcat.TomcatWebServer; -import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory; -import org.springframework.boot.web.server.Ssl; -import org.springframework.boot.web.servlet.ServletContextInitializer; -import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; -import org.springframework.boot.web.servlet.server.Jsp; -import org.springframework.boot.web.servlet.server.Session; -import org.springframework.boot.web.servlet.server.Session.Cookie; -import org.springframework.mock.env.MockEnvironment; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; - -/** - * Tests for {@link DefaultServletWebServerFactoryCustomizer}. - * - * @author Brian Clozel - * @author Yunkun Huang - */ -public class DefaultServletWebServerFactoryCustomizerTests { - - private final ServerProperties properties = new ServerProperties(); - - private DefaultServletWebServerFactoryCustomizer customizer; - - @Captor - private ArgumentCaptor initializersCaptor; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - this.customizer = new DefaultServletWebServerFactoryCustomizer(this.properties); - } - - @Test - public void tomcatAccessLogIsDisabledByDefault() { - TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); - this.customizer.customize(factory); - assertThat(factory.getEngineValves()).isEmpty(); - } - - @Test - public void tomcatAccessLogCanBeEnabled() { - TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); - Map map = new HashMap<>(); - map.put("server.tomcat.accesslog.enabled", "true"); - bindProperties(map); - this.customizer.customize(factory); - assertThat(factory.getEngineValves()).hasSize(1); - assertThat(factory.getEngineValves()).first().isInstanceOf(AccessLogValve.class); - } - - @Test - public void tomcatAccessLogFileDateFormatByDefault() { - TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); - Map map = new HashMap<>(); - map.put("server.tomcat.accesslog.enabled", "true"); - bindProperties(map); - this.customizer.customize(factory); - assertThat(((AccessLogValve) factory.getEngineValves().iterator().next()) - .getFileDateFormat()).isEqualTo(".yyyy-MM-dd"); - } - - @Test - public void tomcatAccessLogFileDateFormatCanBeRedefined() { - TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); - Map map = new HashMap<>(); - map.put("server.tomcat.accesslog.enabled", "true"); - map.put("server.tomcat.accesslog.file-date-format", "yyyy-MM-dd.HH"); - bindProperties(map); - this.customizer.customize(factory); - assertThat(((AccessLogValve) factory.getEngineValves().iterator().next()) - .getFileDateFormat()).isEqualTo("yyyy-MM-dd.HH"); - } - - @Test - public void tomcatAccessLogIsBufferedByDefault() { - TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); - Map map = new HashMap<>(); - map.put("server.tomcat.accesslog.enabled", "true"); - bindProperties(map); - this.customizer.customize(factory); - assertThat(((AccessLogValve) factory.getEngineValves().iterator().next()) - .isBuffered()).isTrue(); - } - - @Test - public void tomcatAccessLogBufferingCanBeDisabled() { - TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); - Map map = new HashMap<>(); - map.put("server.tomcat.accesslog.enabled", "true"); - map.put("server.tomcat.accesslog.buffered", "false"); - bindProperties(map); - this.customizer.customize(factory); - assertThat(((AccessLogValve) factory.getEngineValves().iterator().next()) - .isBuffered()).isFalse(); - } - - @Test - public void redirectContextRootCanBeConfigured() { - Map map = new HashMap<>(); - map.put("server.tomcat.redirect-context-root", "false"); - bindProperties(map); - ServerProperties.Tomcat tomcat = this.properties.getTomcat(); - assertThat(tomcat.getRedirectContextRoot()).isEqualTo(false); - TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); - this.customizer.customize(factory); - Context context = (Context) ((TomcatWebServer) factory.getWebServer()).getTomcat() - .getHost().findChildren()[0]; - assertThat(context.getMapperContextRootRedirectEnabled()).isFalse(); - } - - @Test - public void useRelativeRedirectsCanBeConfigured() { - Map map = new HashMap<>(); - map.put("server.tomcat.use-relative-redirects", "true"); - bindProperties(map); - ServerProperties.Tomcat tomcat = this.properties.getTomcat(); - assertThat(tomcat.getUseRelativeRedirects()).isTrue(); - TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); - this.customizer.customize(factory); - Context context = (Context) ((TomcatWebServer) factory.getWebServer()).getTomcat() - .getHost().findChildren()[0]; - assertThat(context.getUseRelativeRedirects()).isTrue(); - } - - @Test - public void errorReportValveIsConfiguredToNotReportStackTraces() { - TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); - Map map = new HashMap(); - bindProperties(map); - this.customizer.customize(factory); - Valve[] valves = ((TomcatWebServer) factory.getWebServer()).getTomcat().getHost() - .getPipeline().getValves(); - assertThat(valves).hasAtLeastOneElementOfType(ErrorReportValve.class); - for (Valve valve : valves) { - if (valve instanceof ErrorReportValve) { - ErrorReportValve errorReportValve = (ErrorReportValve) valve; - assertThat(errorReportValve.isShowReport()).isFalse(); - assertThat(errorReportValve.isShowServerInfo()).isFalse(); - } - } - } - - @Test - public void testCustomizeTomcat() { - ConfigurableServletWebServerFactory factory = mock( - ConfigurableServletWebServerFactory.class); - this.customizer.customize(factory); - verify(factory, never()).setContextPath(""); - } - - @Test - public void testDefaultDisplayName() { - ConfigurableServletWebServerFactory factory = mock( - ConfigurableServletWebServerFactory.class); - this.customizer.customize(factory); - verify(factory).setDisplayName("application"); - } - - @Test - public void testCustomizeDisplayName() { - ConfigurableServletWebServerFactory factory = mock( - ConfigurableServletWebServerFactory.class); - this.properties.getServlet().setApplicationDisplayName("TestName"); - this.customizer.customize(factory); - verify(factory).setDisplayName("TestName"); - } - - @Test - public void testCustomizeSsl() { - ConfigurableServletWebServerFactory factory = mock( - ConfigurableServletWebServerFactory.class); - Ssl ssl = mock(Ssl.class); - this.properties.setSsl(ssl); - this.customizer.customize(factory); - verify(factory).setSsl(ssl); - } - - @Test - public void testCustomizeJsp() { - ConfigurableServletWebServerFactory factory = mock( - ConfigurableServletWebServerFactory.class); - this.customizer.customize(factory); - verify(factory).setJsp(any(Jsp.class)); - } - - @Test - public void customizeSessionProperties() throws Exception { - Map map = new HashMap<>(); - map.put("server.servlet.session.timeout", "123"); - map.put("server.servlet.session.tracking-modes", "cookie,url"); - map.put("server.servlet.session.cookie.name", "testname"); - map.put("server.servlet.session.cookie.domain", "testdomain"); - map.put("server.servlet.session.cookie.path", "/testpath"); - map.put("server.servlet.session.cookie.comment", "testcomment"); - map.put("server.servlet.session.cookie.http-only", "true"); - map.put("server.servlet.session.cookie.secure", "true"); - map.put("server.servlet.session.cookie.max-age", "60"); - bindProperties(map); - ConfigurableServletWebServerFactory factory = mock( - ConfigurableServletWebServerFactory.class); - this.customizer.customize(factory); - ArgumentCaptor sessionCaptor = ArgumentCaptor.forClass(Session.class); - verify(factory).setSession(sessionCaptor.capture()); - assertThat(sessionCaptor.getValue().getTimeout()) - .isEqualTo(Duration.ofSeconds(123)); - Cookie cookie = sessionCaptor.getValue().getCookie(); - assertThat(cookie.getName()).isEqualTo("testname"); - assertThat(cookie.getDomain()).isEqualTo("testdomain"); - assertThat(cookie.getPath()).isEqualTo("/testpath"); - assertThat(cookie.getComment()).isEqualTo("testcomment"); - assertThat(cookie.getHttpOnly()).isTrue(); - assertThat(cookie.getMaxAge()).isEqualTo(Duration.ofSeconds(60)); - - } - - @Test - public void testCustomizeTomcatPort() { - ConfigurableServletWebServerFactory factory = mock( - ConfigurableServletWebServerFactory.class); - this.properties.setPort(8080); - this.customizer.customize(factory); - verify(factory).setPort(8080); - } - - @Test - public void customizeTomcatDisplayName() { - Map map = new HashMap<>(); - map.put("server.servlet.application-display-name", "MyBootApp"); - bindProperties(map); - TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); - this.customizer.customize(factory); - assertThat(factory.getDisplayName()).isEqualTo("MyBootApp"); - } - - @Test - public void disableTomcatRemoteIpValve() { - Map map = new HashMap<>(); - map.put("server.tomcat.remote-ip-header", ""); - map.put("server.tomcat.protocol-header", ""); - bindProperties(map); - TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); - this.customizer.customize(factory); - assertThat(factory.getEngineValves()).isEmpty(); - } - - @Test - public void defaultTomcatBackgroundProcessorDelay() { - TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); - this.customizer.customize(factory); - TomcatWebServer webServer = (TomcatWebServer) factory.getWebServer(); - assertThat(webServer.getTomcat().getEngine().getBackgroundProcessorDelay()) - .isEqualTo(30); - webServer.stop(); - } - - @Test - public void customTomcatBackgroundProcessorDelay() { - Map map = new HashMap<>(); - map.put("server.tomcat.background-processor-delay", "5"); - bindProperties(map); - TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); - this.customizer.customize(factory); - TomcatWebServer webServer = (TomcatWebServer) factory.getWebServer(); - assertThat(webServer.getTomcat().getEngine().getBackgroundProcessorDelay()) - .isEqualTo(5); - webServer.stop(); - } - - @Test - public void defaultTomcatRemoteIpValve() { - Map map = new HashMap<>(); - // Since 1.1.7 you need to specify at least the protocol - map.put("server.tomcat.protocol-header", "X-Forwarded-Proto"); - map.put("server.tomcat.remote-ip-header", "X-Forwarded-For"); - bindProperties(map); - testRemoteIpValveConfigured(); - } - - @Test - public void setUseForwardHeadersTomcat() { - // Since 1.3.0 no need to explicitly set header names if use-forward-header=true - this.properties.setUseForwardHeaders(true); - testRemoteIpValveConfigured(); - } - - @Test - public void deduceUseForwardHeadersTomcat() { - this.customizer.setEnvironment(new MockEnvironment().withProperty("DYNO", "-")); - testRemoteIpValveConfigured(); - } - - private void testRemoteIpValveConfigured() { - TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); - this.customizer.customize(factory); - assertThat(factory.getEngineValves()).hasSize(1); - Valve valve = factory.getEngineValves().iterator().next(); - assertThat(valve).isInstanceOf(RemoteIpValve.class); - RemoteIpValve remoteIpValve = (RemoteIpValve) valve; - assertThat(remoteIpValve.getProtocolHeader()).isEqualTo("X-Forwarded-Proto"); - assertThat(remoteIpValve.getProtocolHeaderHttpsValue()).isEqualTo("https"); - assertThat(remoteIpValve.getRemoteIpHeader()).isEqualTo("X-Forwarded-For"); - String expectedInternalProxies = "10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" // 10/8 - + "192\\.168\\.\\d{1,3}\\.\\d{1,3}|" // 192.168/16 - + "169\\.254\\.\\d{1,3}\\.\\d{1,3}|" // 169.254/16 - + "127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" // 127/8 - + "172\\.1[6-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" // 172.16/12 - + "172\\.2[0-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" - + "172\\.3[0-1]{1}\\.\\d{1,3}\\.\\d{1,3}"; - assertThat(remoteIpValve.getInternalProxies()).isEqualTo(expectedInternalProxies); - } - - @Test - public void customTomcatRemoteIpValve() { - Map map = new HashMap<>(); - map.put("server.tomcat.remote-ip-header", "x-my-remote-ip-header"); - map.put("server.tomcat.protocol-header", "x-my-protocol-header"); - map.put("server.tomcat.internal-proxies", "192.168.0.1"); - map.put("server.tomcat.port-header", "x-my-forward-port"); - map.put("server.tomcat.protocol-header-https-value", "On"); - bindProperties(map); - TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); - this.customizer.customize(factory); - assertThat(factory.getEngineValves()).hasSize(1); - Valve valve = factory.getEngineValves().iterator().next(); - assertThat(valve).isInstanceOf(RemoteIpValve.class); - RemoteIpValve remoteIpValve = (RemoteIpValve) valve; - assertThat(remoteIpValve.getProtocolHeader()).isEqualTo("x-my-protocol-header"); - assertThat(remoteIpValve.getProtocolHeaderHttpsValue()).isEqualTo("On"); - assertThat(remoteIpValve.getRemoteIpHeader()).isEqualTo("x-my-remote-ip-header"); - assertThat(remoteIpValve.getPortHeader()).isEqualTo("x-my-forward-port"); - assertThat(remoteIpValve.getInternalProxies()).isEqualTo("192.168.0.1"); - } - - @Test - public void customTomcatAcceptCount() { - Map map = new HashMap<>(); - map.put("server.tomcat.accept-count", "10"); - bindProperties(map); - TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(0); - this.customizer.customize(factory); - TomcatWebServer server = (TomcatWebServer) factory.getWebServer(); - server.start(); - try { - assertThat(((AbstractProtocol) server.getTomcat().getConnector() - .getProtocolHandler()).getAcceptCount()).isEqualTo(10); - } - finally { - server.stop(); - } - } - - @Test - public void customTomcatMaxConnections() { - Map map = new HashMap<>(); - map.put("server.tomcat.max-connections", "5"); - bindProperties(map); - TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(0); - this.customizer.customize(factory); - TomcatWebServer server = (TomcatWebServer) factory.getWebServer(); - server.start(); - try { - assertThat(((AbstractProtocol) server.getTomcat().getConnector() - .getProtocolHandler()).getMaxConnections()).isEqualTo(5); - } - finally { - server.stop(); - } - } - - @Test - public void customTomcatMaxHttpPostSize() { - Map map = new HashMap<>(); - map.put("server.tomcat.max-http-post-size", "10000"); - bindProperties(map); - TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(0); - this.customizer.customize(factory); - TomcatWebServer server = (TomcatWebServer) factory.getWebServer(); - server.start(); - try { - assertThat(server.getTomcat().getConnector().getMaxPostSize()) - .isEqualTo(10000); - } - finally { - server.stop(); - } - } - - @Test - public void customTomcatDisableMaxHttpPostSize() { - Map map = new HashMap<>(); - map.put("server.tomcat.max-http-post-size", "-1"); - bindProperties(map); - TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(0); - this.customizer.customize(factory); - TomcatWebServer server = (TomcatWebServer) factory.getWebServer(); - server.start(); - try { - assertThat(server.getTomcat().getConnector().getMaxPostSize()).isEqualTo(-1); - } - finally { - server.stop(); - } - } - - @Test - public void customizeUndertowAccessLog() { - Map map = new HashMap<>(); - map.put("server.undertow.accesslog.enabled", "true"); - map.put("server.undertow.accesslog.pattern", "foo"); - map.put("server.undertow.accesslog.prefix", "test_log"); - map.put("server.undertow.accesslog.suffix", "txt"); - map.put("server.undertow.accesslog.dir", "test-logs"); - map.put("server.undertow.accesslog.rotate", "false"); - bindProperties(map); - UndertowServletWebServerFactory factory = spy( - new UndertowServletWebServerFactory()); - this.customizer.customize(factory); - verify(factory).setAccessLogEnabled(true); - verify(factory).setAccessLogPattern("foo"); - verify(factory).setAccessLogPrefix("test_log"); - verify(factory).setAccessLogSuffix("txt"); - verify(factory).setAccessLogDirectory(new File("test-logs")); - verify(factory).setAccessLogRotate(false); - } - - @Test - public void testCustomizeTomcatMinSpareThreads() { - Map map = new HashMap<>(); - map.put("server.tomcat.min-spare-threads", "10"); - bindProperties(map); - assertThat(this.properties.getTomcat().getMinSpareThreads()).isEqualTo(10); - } - - @Test - public void customTomcatTldSkip() { - Map map = new HashMap<>(); - map.put("server.tomcat.additional-tld-skip-patterns", "foo.jar,bar.jar"); - bindProperties(map); - testCustomTomcatTldSkip("foo.jar", "bar.jar"); - } - - @Test - public void customTomcatTldSkipAsList() { - Map map = new HashMap<>(); - map.put("server.tomcat.additional-tld-skip-patterns[0]", "biz.jar"); - map.put("server.tomcat.additional-tld-skip-patterns[1]", "bah.jar"); - bindProperties(map); - testCustomTomcatTldSkip("biz.jar", "bah.jar"); - } - - private void testCustomTomcatTldSkip(String... expectedJars) { - TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); - this.customizer.customize(factory); - assertThat(factory.getTldSkipPatterns()).contains(expectedJars); - assertThat(factory.getTldSkipPatterns()).contains("junit-*.jar", - "spring-boot-*.jar"); - } - - @Test - public void defaultUseForwardHeadersUndertow() { - UndertowServletWebServerFactory factory = spy( - new UndertowServletWebServerFactory()); - this.customizer.customize(factory); - verify(factory).setUseForwardHeaders(false); - } - - @Test - public void setUseForwardHeadersUndertow() { - this.properties.setUseForwardHeaders(true); - UndertowServletWebServerFactory factory = spy( - new UndertowServletWebServerFactory()); - this.customizer.customize(factory); - verify(factory).setUseForwardHeaders(true); - } - - @Test - public void deduceUseForwardHeadersUndertow() { - this.customizer.setEnvironment(new MockEnvironment().withProperty("DYNO", "-")); - UndertowServletWebServerFactory factory = spy( - new UndertowServletWebServerFactory()); - this.customizer.customize(factory); - verify(factory).setUseForwardHeaders(true); - } - - @Test - public void defaultUseForwardHeadersJetty() { - JettyServletWebServerFactory factory = spy(new JettyServletWebServerFactory()); - this.customizer.customize(factory); - verify(factory).setUseForwardHeaders(false); - } - - @Test - public void setUseForwardHeadersJetty() { - this.properties.setUseForwardHeaders(true); - JettyServletWebServerFactory factory = spy(new JettyServletWebServerFactory()); - this.customizer.customize(factory); - verify(factory).setUseForwardHeaders(true); - } - - @Test - public void deduceUseForwardHeadersJetty() { - this.customizer.setEnvironment(new MockEnvironment().withProperty("DYNO", "-")); - JettyServletWebServerFactory factory = spy(new JettyServletWebServerFactory()); - this.customizer.customize(factory); - verify(factory).setUseForwardHeaders(true); - } - - @Test - public void sessionStoreDir() { - Map map = new HashMap<>(); - map.put("server.servlet.session.store-dir", "myfolder"); - bindProperties(map); - JettyServletWebServerFactory factory = spy(new JettyServletWebServerFactory()); - this.customizer.customize(factory); - ArgumentCaptor sessionCaptor = ArgumentCaptor.forClass(Session.class); - verify(factory).setSession(sessionCaptor.capture()); - assertThat(sessionCaptor.getValue().getStoreDir()) - .isEqualTo(new File("myfolder")); - } - - @Test - public void jettyAccessLogCanBeEnabled() { - JettyServletWebServerFactory factory = new JettyServletWebServerFactory(0); - Map map = new HashMap<>(); - map.put("server.jetty.accesslog.enabled", "true"); - bindProperties(map); - this.customizer.customize(factory); - JettyWebServer webServer = (JettyWebServer) factory.getWebServer(); - try { - NCSARequestLog requestLog = getNCSARequestLog(webServer); - assertThat(requestLog.getFilename()).isNull(); - assertThat(requestLog.isAppend()).isFalse(); - assertThat(requestLog.isExtended()).isFalse(); - assertThat(requestLog.getLogCookies()).isFalse(); - assertThat(requestLog.getLogServer()).isFalse(); - assertThat(requestLog.getLogLatency()).isFalse(); - } - finally { - webServer.stop(); - } - } - - @Test - public void jettyAccessLogCanBeCustomized() throws IOException { - File logFile = File.createTempFile("jetty_log", ".log"); - JettyServletWebServerFactory factory = new JettyServletWebServerFactory(0); - Map map = new HashMap<>(); - String timezone = TimeZone.getDefault().getID(); - map.put("server.jetty.accesslog.enabled", "true"); - map.put("server.jetty.accesslog.filename", logFile.getAbsolutePath()); - map.put("server.jetty.accesslog.file-date-format", "yyyy-MM-dd"); - map.put("server.jetty.accesslog.retention-period", "42"); - map.put("server.jetty.accesslog.append", "true"); - map.put("server.jetty.accesslog.extended-format", "true"); - map.put("server.jetty.accesslog.date-format", "HH:mm:ss"); - map.put("server.jetty.accesslog.locale", "en_BE"); - map.put("server.jetty.accesslog.time-zone", timezone); - map.put("server.jetty.accesslog.log-cookies", "true"); - map.put("server.jetty.accesslog.log-server", "true"); - map.put("server.jetty.accesslog.log-latency", "true"); - bindProperties(map); - this.customizer.customize(factory); - JettyWebServer webServer = (JettyWebServer) factory.getWebServer(); - NCSARequestLog requestLog = getNCSARequestLog(webServer); - try { - assertThat(requestLog.getFilename()).isEqualTo(logFile.getAbsolutePath()); - assertThat(requestLog.getFilenameDateFormat()).isEqualTo("yyyy-MM-dd"); - assertThat(requestLog.getRetainDays()).isEqualTo(42); - assertThat(requestLog.isAppend()).isTrue(); - assertThat(requestLog.isExtended()).isTrue(); - assertThat(requestLog.getLogDateFormat()).isEqualTo("HH:mm:ss"); - assertThat(requestLog.getLogLocale()).isEqualTo(new Locale("en", "BE")); - assertThat(requestLog.getLogTimeZone()).isEqualTo(timezone); - assertThat(requestLog.getLogCookies()).isTrue(); - assertThat(requestLog.getLogServer()).isTrue(); - assertThat(requestLog.getLogLatency()).isTrue(); - } - finally { - webServer.stop(); - } - } - - private NCSARequestLog getNCSARequestLog(JettyWebServer webServer) { - RequestLog requestLog = webServer.getServer().getRequestLog(); - assertThat(requestLog).isInstanceOf(NCSARequestLog.class); - return (NCSARequestLog) requestLog; - } - - @Test - public void skipNullElementsForUndertow() { - UndertowServletWebServerFactory factory = mock( - UndertowServletWebServerFactory.class); - this.customizer.customize(factory); - verify(factory, never()).setAccessLogEnabled(anyBoolean()); - } - - @Test - public void customTomcatStaticResourceCacheTtl() { - Map map = new HashMap<>(); - map.put("server.tomcat.resource.cache-ttl", "10000"); - bindProperties(map); - TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(0); - this.customizer.customize(factory); - TomcatWebServer server = (TomcatWebServer) factory.getWebServer(); - server.start(); - try { - Tomcat tomcat = server.getTomcat(); - Context context = (Context) tomcat.getHost().findChildren()[0]; - assertThat(context.getResources().getCacheTtl()).isEqualTo(10000L); - } - finally { - server.stop(); - } - } - - private void bindProperties(Map map) { - ConfigurationPropertySource source = new MapConfigurationPropertySource(map); - new Binder(source).bind("server", Bindable.ofInstance(this.properties)); - } - -} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryCustomizerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryCustomizerTests.java new file mode 100644 index 00000000000..6b1a06be771 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryCustomizerTests.java @@ -0,0 +1,181 @@ +/* + * Copyright 2012-2018 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.servlet; + +import java.io.File; +import java.time.Duration; +import java.util.HashMap; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.MockitoAnnotations; + +import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.boot.context.properties.bind.Bindable; +import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.boot.context.properties.source.ConfigurationPropertySource; +import org.springframework.boot.context.properties.source.MapConfigurationPropertySource; +import org.springframework.boot.web.server.Ssl; +import org.springframework.boot.web.servlet.ServletContextInitializer; +import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; +import org.springframework.boot.web.servlet.server.Jsp; +import org.springframework.boot.web.servlet.server.Session; +import org.springframework.boot.web.servlet.server.Session.Cookie; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +/** + * Tests for {@link ServletWebServerFactoryCustomizer}. + * + * @author Brian Clozel + * @author Yunkun Huang + */ +public class ServletWebServerFactoryCustomizerTests { + + private final ServerProperties properties = new ServerProperties(); + + private ServletWebServerFactoryCustomizer customizer; + + @Captor + private ArgumentCaptor initializersCaptor; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + this.customizer = new ServletWebServerFactoryCustomizer( + this.properties); + } + + @Test + public void testDefaultDisplayName() { + ConfigurableServletWebServerFactory factory = mock( + ConfigurableServletWebServerFactory.class); + this.customizer.customize(factory); + verify(factory).setDisplayName("application"); + } + + @Test + public void testCustomizeDisplayName() { + ConfigurableServletWebServerFactory factory = mock( + ConfigurableServletWebServerFactory.class); + this.properties.getServlet().setApplicationDisplayName("TestName"); + this.customizer.customize(factory); + verify(factory).setDisplayName("TestName"); + } + + @Test + public void testCustomizeSsl() { + ConfigurableServletWebServerFactory factory = mock( + ConfigurableServletWebServerFactory.class); + Ssl ssl = mock(Ssl.class); + this.properties.setSsl(ssl); + this.customizer.customize(factory); + verify(factory).setSsl(ssl); + } + + @Test + public void testCustomizeJsp() { + ConfigurableServletWebServerFactory factory = mock( + ConfigurableServletWebServerFactory.class); + this.customizer.customize(factory); + verify(factory).setJsp(any(Jsp.class)); + } + + @Test + public void customizeSessionProperties() throws Exception { + Map map = new HashMap<>(); + map.put("server.servlet.session.timeout", "123"); + map.put("server.servlet.session.tracking-modes", "cookie,url"); + map.put("server.servlet.session.cookie.name", "testname"); + map.put("server.servlet.session.cookie.domain", "testdomain"); + map.put("server.servlet.session.cookie.path", "/testpath"); + map.put("server.servlet.session.cookie.comment", "testcomment"); + map.put("server.servlet.session.cookie.http-only", "true"); + map.put("server.servlet.session.cookie.secure", "true"); + map.put("server.servlet.session.cookie.max-age", "60"); + bindProperties(map); + ConfigurableServletWebServerFactory factory = mock( + ConfigurableServletWebServerFactory.class); + this.customizer.customize(factory); + ArgumentCaptor sessionCaptor = ArgumentCaptor.forClass(Session.class); + verify(factory).setSession(sessionCaptor.capture()); + assertThat(sessionCaptor.getValue().getTimeout()) + .isEqualTo(Duration.ofSeconds(123)); + Cookie cookie = sessionCaptor.getValue().getCookie(); + assertThat(cookie.getName()).isEqualTo("testname"); + assertThat(cookie.getDomain()).isEqualTo("testdomain"); + assertThat(cookie.getPath()).isEqualTo("/testpath"); + assertThat(cookie.getComment()).isEqualTo("testcomment"); + assertThat(cookie.getHttpOnly()).isTrue(); + assertThat(cookie.getMaxAge()).isEqualTo(Duration.ofSeconds(60)); + + } + + @Test + public void testCustomizeTomcatPort() { + ConfigurableServletWebServerFactory factory = mock( + ConfigurableServletWebServerFactory.class); + this.properties.setPort(8080); + this.customizer.customize(factory); + verify(factory).setPort(8080); + } + + @Test + public void customizeServletDisplayName() { + Map map = new HashMap<>(); + map.put("server.servlet.application-display-name", "MyBootApp"); + bindProperties(map); + ConfigurableServletWebServerFactory factory = mock( + ConfigurableServletWebServerFactory.class); + this.customizer.customize(factory); + verify(factory).setDisplayName("MyBootApp"); + } + + @Test + public void testCustomizeTomcatMinSpareThreads() { + Map map = new HashMap<>(); + map.put("server.tomcat.min-spare-threads", "10"); + bindProperties(map); + assertThat(this.properties.getTomcat().getMinSpareThreads()).isEqualTo(10); + } + + @Test + public void sessionStoreDir() { + Map map = new HashMap<>(); + map.put("server.servlet.session.store-dir", "myfolder"); + bindProperties(map); + ConfigurableServletWebServerFactory factory = mock( + ConfigurableServletWebServerFactory.class); + this.customizer.customize(factory); + ArgumentCaptor sessionCaptor = ArgumentCaptor.forClass(Session.class); + verify(factory).setSession(sessionCaptor.capture()); + assertThat(sessionCaptor.getValue().getStoreDir()) + .isEqualTo(new File("myfolder")); + } + + private void bindProperties(Map map) { + ConfigurationPropertySource source = new MapConfigurationPropertySource(map); + new Binder(source).bind("server", Bindable.ofInstance(this.properties)); + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/TomcatServletWebServerFactoryCustomizerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/TomcatServletWebServerFactoryCustomizerTests.java new file mode 100644 index 00000000000..9163464ba23 --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/TomcatServletWebServerFactoryCustomizerTests.java @@ -0,0 +1,112 @@ +/* + * Copyright 2012-2018 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.servlet; + +import org.apache.catalina.Context; +import org.junit.Before; +import org.junit.Test; + +import org.springframework.boot.autoconfigure.web.ServerProperties; +import org.springframework.boot.context.properties.bind.Bindable; +import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.boot.context.properties.source.ConfigurationPropertySources; +import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; +import org.springframework.boot.web.embedded.tomcat.TomcatWebServer; +import org.springframework.mock.env.MockEnvironment; +import org.springframework.test.context.support.TestPropertySourceUtils; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link TomcatServletWebServerFactoryCustomizer}. + * + * @author Phillip Webb + */ +public class TomcatServletWebServerFactoryCustomizerTests { + + private TomcatServletWebServerFactoryCustomizer customizer; + + private MockEnvironment environment; + + private ServerProperties serverProperties; + + @Before + public void setup() { + this.environment = new MockEnvironment(); + this.serverProperties = new ServerProperties(); + ConfigurationPropertySources.attach(this.environment); + this.customizer = new TomcatServletWebServerFactoryCustomizer( + this.serverProperties); + } + + @Test + public void customTldSkip() { + bind("server.tomcat.additional-tld-skip-patterns=foo.jar,bar.jar"); + testCustomTldSkip("foo.jar", "bar.jar"); + } + + @Test + public void customTldSkipAsList() { + bind("server.tomcat.additional-tld-skip-patterns[0]=biz.jar", + "server.tomcat.additional-tld-skip-patterns[1]=bah.jar"); + testCustomTldSkip("biz.jar", "bah.jar"); + } + + private void testCustomTldSkip(String... expectedJars) { + TomcatServletWebServerFactory factory = customizeAndGetFactory(); + assertThat(factory.getTldSkipPatterns()).contains(expectedJars); + assertThat(factory.getTldSkipPatterns()).contains("junit-*.jar", + "spring-boot-*.jar"); + } + + @Test + public void redirectContextRootCanBeConfigured() { + bind("server.tomcat.redirect-context-root=false"); + ServerProperties.Tomcat tomcat = this.serverProperties.getTomcat(); + assertThat(tomcat.getRedirectContextRoot()).isEqualTo(false); + TomcatWebServer server = customizeAndGetServer(); + Context context = (Context) server.getTomcat().getHost().findChildren()[0]; + assertThat(context.getMapperContextRootRedirectEnabled()).isFalse(); + } + + @Test + public void useRelativeRedirectsCanBeConfigured() { + bind("server.tomcat.use-relative-redirects=true"); + assertThat(this.serverProperties.getTomcat().getUseRelativeRedirects()).isTrue(); + TomcatWebServer server = customizeAndGetServer(); + Context context = (Context) server.getTomcat().getHost().findChildren()[0]; + assertThat(context.getUseRelativeRedirects()).isTrue(); + } + + private void bind(String... inlinedProperties) { + TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.environment, + inlinedProperties); + new Binder(ConfigurationPropertySources.get(this.environment)).bind("server", + Bindable.ofInstance(this.serverProperties)); + } + + private TomcatWebServer customizeAndGetServer() { + TomcatServletWebServerFactory factory = customizeAndGetFactory(); + return (TomcatWebServer) factory.getWebServer(); + } + + private TomcatServletWebServerFactory customizeAndGetFactory() { + TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(0); + this.customizer.customize(factory); + return factory; + } +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/error/BasicErrorControllerIntegrationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/error/BasicErrorControllerIntegrationTests.java index 5a06f85e7e2..0e41559e780 100755 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/error/BasicErrorControllerIntegrationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/error/BasicErrorControllerIntegrationTests.java @@ -234,8 +234,7 @@ public class BasicErrorControllerIntegrationTests { @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented - @Import({ ServletWebServerFactoryAutoConfiguration.EmbeddedTomcat.class, - ServletWebServerFactoryAutoConfiguration.class, + @Import({ ServletWebServerFactoryAutoConfiguration.class, DispatcherServletAutoConfiguration.class, WebMvcAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class, ErrorMvcAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class }) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/error/BasicErrorControllerMockMvcTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/error/BasicErrorControllerMockMvcTests.java index 1bc0dfc500b..5dacfd3b953 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/error/BasicErrorControllerMockMvcTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/error/BasicErrorControllerMockMvcTests.java @@ -129,8 +129,7 @@ public class BasicErrorControllerMockMvcTests { @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented - @Import({ ServletWebServerFactoryAutoConfiguration.EmbeddedTomcat.class, - ServletWebServerFactoryAutoConfiguration.class, + @Import({ ServletWebServerFactoryAutoConfiguration.class, DispatcherServletAutoConfiguration.class, WebMvcAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class, ErrorMvcAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class }) diff --git a/spring-boot-project/spring-boot-parent/src/checkstyle/import-control.xml b/spring-boot-project/spring-boot-parent/src/checkstyle/import-control.xml index 78d3c74eaf7..a398cbffcee 100644 --- a/spring-boot-project/spring-boot-parent/src/checkstyle/import-control.xml +++ b/spring-boot-project/spring-boot-parent/src/checkstyle/import-control.xml @@ -14,14 +14,8 @@ - - - - - - - - + + diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/ConfigurableJettyWebServerFactory.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/ConfigurableJettyWebServerFactory.java index 47010fbc734..c60fe54a5e1 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/ConfigurableJettyWebServerFactory.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/jetty/ConfigurableJettyWebServerFactory.java @@ -18,15 +18,17 @@ package org.springframework.boot.web.embedded.jetty; import org.eclipse.jetty.server.Server; +import org.springframework.boot.web.server.ConfigurableWebServerFactory; + /** - * Web Server Factory configuration for Jetty-specific features. + * {@link ConfigurableWebServerFactory} for Jetty-specific features. * * @author Brian Clozel * @since 2.0.0 * @see JettyServletWebServerFactory * @see JettyReactiveWebServerFactory */ -public interface ConfigurableJettyWebServerFactory { +public interface ConfigurableJettyWebServerFactory extends ConfigurableWebServerFactory { /** * Set the number of acceptor threads to use. diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/ConfigurableTomcatWebServerFactory.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/ConfigurableTomcatWebServerFactory.java index f686173d72e..b0a0a208c42 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/ConfigurableTomcatWebServerFactory.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/ConfigurableTomcatWebServerFactory.java @@ -24,15 +24,17 @@ import org.apache.catalina.Engine; import org.apache.catalina.Valve; import org.apache.catalina.connector.Connector; +import org.springframework.boot.web.server.ConfigurableWebServerFactory; + /** - * Web Server Factory configuration for Tomcat-specific features. + * {@link ConfigurableWebServerFactory} for Tomcat-specific features. * * @author Brian Clozel * @since 2.0.0 * @see TomcatServletWebServerFactory * @see TomcatReactiveWebServerFactory */ -public interface ConfigurableTomcatWebServerFactory { +public interface ConfigurableTomcatWebServerFactory extends ConfigurableWebServerFactory { /** * Set the Tomcat base directory. If not specified a temporary directory will be used. diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/ConfigurableUndertowWebServerFactory.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/ConfigurableUndertowWebServerFactory.java index a3b30c62851..874e4253571 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/ConfigurableUndertowWebServerFactory.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/undertow/ConfigurableUndertowWebServerFactory.java @@ -21,15 +21,18 @@ import java.io.File; import io.undertow.Undertow.Builder; import io.undertow.servlet.api.DeploymentInfo; +import org.springframework.boot.web.server.ConfigurableWebServerFactory; + /** - * Web Server Factory configuration for Undertow-specific features. + * {@link ConfigurableWebServerFactory} for Undertow-specific features. * * @author Brian Clozel * @since 2.0.0 * @see UndertowServletWebServerFactory * @see UndertowReactiveWebServerFactory */ -public interface ConfigurableUndertowWebServerFactory { +public interface ConfigurableUndertowWebServerFactory + extends ConfigurableWebServerFactory { /** * Add {@link UndertowBuilderCustomizer}s that should be used to customize the