Move web server auto-configure classes into spring-boot-web-server

This commit is contained in:
Andy Wilkinson
2025-05-07 13:49:13 +01:00
committed by Phillip Webb
parent dee54a8b99
commit 4471c0e644
89 changed files with 360 additions and 436 deletions

View File

@@ -0,0 +1,152 @@
/*
* Copyright 2012-2025 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.autoconfigure.web;
import java.net.InetAddress;
import java.util.Collections;
import java.util.Map;
import org.junit.jupiter.api.Test;
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.MimeMappings;
import org.springframework.boot.web.server.MimeMappings.Mapping;
import org.springframework.boot.web.server.autoconfigure.ServerProperties;
import org.springframework.util.unit.DataSize;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link ServerProperties}.
*
* @author Dave Syer
* @author Stephane Nicoll
* @author Andy Wilkinson
* @author Phillip Webb
* @author Eddú Meléndez
* @author Quinten De Swaef
* @author Venil Noronha
* @author Andrew McGhie
* @author HaiTao Zhang
* @author Rafiullah Hamedy
* @author Chris Bono
* @author Parviz Rozikov
* @author Lasse Wulff
* @author Moritz Halbritter
*/
class ServerPropertiesTests {
private final ServerProperties properties = new ServerProperties();
@Test
void testAddressBinding() throws Exception {
bind("server.address", "127.0.0.1");
assertThat(this.properties.getAddress()).isEqualTo(InetAddress.getByName("127.0.0.1"));
}
@Test
void testPortBinding() {
bind("server.port", "9000");
assertThat(this.properties.getPort().intValue()).isEqualTo(9000);
}
@Test
void testServerHeaderDefault() {
assertThat(this.properties.getServerHeader()).isNull();
}
@Test
void testServerHeader() {
bind("server.server-header", "Custom Server");
assertThat(this.properties.getServerHeader()).isEqualTo("Custom Server");
}
@Test
void testTrailingSlashOfContextPathIsRemoved() {
bind("server.servlet.context-path", "/foo/");
assertThat(this.properties.getServlet().getContextPath()).isEqualTo("/foo");
}
@Test
void testSlashOfContextPathIsDefaultValue() {
bind("server.servlet.context-path", "/");
assertThat(this.properties.getServlet().getContextPath()).isEmpty();
}
@Test
void testContextPathWithLeadingWhitespace() {
bind("server.servlet.context-path", " /assets");
assertThat(this.properties.getServlet().getContextPath()).isEqualTo("/assets");
}
@Test
void testContextPathWithTrailingWhitespace() {
bind("server.servlet.context-path", "/assets/copy/ ");
assertThat(this.properties.getServlet().getContextPath()).isEqualTo("/assets/copy");
}
@Test
void testContextPathWithLeadingAndTrailingWhitespace() {
bind("server.servlet.context-path", " /assets ");
assertThat(this.properties.getServlet().getContextPath()).isEqualTo("/assets");
}
@Test
void testContextPathWithLeadingAndTrailingWhitespaceAndContextWithSpace() {
bind("server.servlet.context-path", " /assets /copy/ ");
assertThat(this.properties.getServlet().getContextPath()).isEqualTo("/assets /copy");
}
@Test
void testDefaultMimeMapping() {
assertThat(this.properties.getMimeMappings()).isEmpty();
}
@Test
void testCustomizedMimeMapping() {
MimeMappings expectedMappings = new MimeMappings();
expectedMappings.add("mjs", "text/javascript");
bind("server.mime-mappings.mjs", "text/javascript");
assertThat(this.properties.getMimeMappings())
.containsExactly(expectedMappings.getAll().toArray(new Mapping[0]));
}
@Test
void testCustomizeMaxHttpRequestHeaderSize() {
bind("server.max-http-request-header-size", "1MB");
assertThat(this.properties.getMaxHttpRequestHeaderSize()).isEqualTo(DataSize.ofMegabytes(1));
}
@Test
void testCustomizeMaxHttpRequestHeaderSizeUseBytesByDefault() {
bind("server.max-http-request-header-size", "1024");
assertThat(this.properties.getMaxHttpRequestHeaderSize()).isEqualTo(DataSize.ofKilobytes(1));
}
private void bind(String name, String value) {
bind(Collections.singletonMap(name, value));
}
private void bind(Map<String, String> map) {
ConfigurationPropertySource source = new MapConfigurationPropertySource(map);
new Binder(source).bind("server", Bindable.ofInstance(this.properties));
}
}

View File

@@ -0,0 +1,91 @@
/*
* Copyright 2012-2025 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
*
* https://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.web.server.autoconfigure.reactive;
import java.net.InetAddress;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.ssl.DefaultSslBundleRegistry;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.boot.web.server.Shutdown;
import org.springframework.boot.web.server.Ssl;
import org.springframework.boot.web.server.autoconfigure.ServerProperties;
import org.springframework.boot.web.server.reactive.ConfigurableReactiveWebServerFactory;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.assertArg;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link ReactiveWebServerFactoryCustomizer}.
*
* @author Brian Clozel
* @author Yunkun Huang
* @author Scott Frederick
*/
class ReactiveWebServerFactoryCustomizerTests {
private final ServerProperties properties = new ServerProperties();
private final SslBundles sslBundles = new DefaultSslBundleRegistry();
private ReactiveWebServerFactoryCustomizer customizer;
@BeforeEach
void setup() {
this.customizer = new ReactiveWebServerFactoryCustomizer(this.properties, this.sslBundles);
}
@Test
void testCustomizeServerPort() {
ConfigurableReactiveWebServerFactory factory = mock(ConfigurableReactiveWebServerFactory.class);
this.properties.setPort(9000);
this.customizer.customize(factory);
then(factory).should().setPort(9000);
}
@Test
void testCustomizeServerAddress() {
ConfigurableReactiveWebServerFactory factory = mock(ConfigurableReactiveWebServerFactory.class);
InetAddress address = InetAddress.getLoopbackAddress();
this.properties.setAddress(address);
this.customizer.customize(factory);
then(factory).should().setAddress(address);
}
@Test
void testCustomizeServerSsl() {
ConfigurableReactiveWebServerFactory factory = mock(ConfigurableReactiveWebServerFactory.class);
Ssl ssl = mock(Ssl.class);
this.properties.setSsl(ssl);
this.customizer.customize(factory);
then(factory).should().setSsl(ssl);
then(factory).should().setSslBundles(this.sslBundles);
}
@Test
void whenShutdownPropertyIsSetThenShutdownIsCustomized() {
this.properties.setShutdown(Shutdown.GRACEFUL);
ConfigurableReactiveWebServerFactory factory = mock(ConfigurableReactiveWebServerFactory.class);
this.customizer.customize(factory);
then(factory).should().setShutdown(assertArg((shutdown) -> assertThat(shutdown).isEqualTo(Shutdown.GRACEFUL)));
}
}

View File

@@ -0,0 +1,217 @@
/*
* Copyright 2012-2025 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
*
* https://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.web.server.autoconfigure.servlet;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
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.Cookie;
import org.springframework.boot.web.server.MimeMappings;
import org.springframework.boot.web.server.Shutdown;
import org.springframework.boot.web.server.Ssl;
import org.springframework.boot.web.server.autoconfigure.ServerProperties;
import org.springframework.boot.web.server.servlet.ConfigurableServletWebServerFactory;
import org.springframework.boot.web.server.servlet.Jsp;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.assertArg;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
/**
* Tests for {@link ServletWebServerFactoryCustomizer}.
*
* @author Brian Clozel
* @author Yunkun Huang
* @author Lasse Wulff
*/
class ServletWebServerFactoryCustomizerTests {
private final ServerProperties properties = new ServerProperties();
private ServletWebServerFactoryCustomizer customizer;
@BeforeEach
void setup() {
this.customizer = new ServletWebServerFactoryCustomizer(this.properties);
}
@Test
void testDefaultDisplayName() {
ConfigurableServletWebServerFactory factory = mock(ConfigurableServletWebServerFactory.class);
this.customizer.customize(factory);
then(factory).should().setDisplayName("application");
}
@Test
void testCustomizeDisplayName() {
ConfigurableServletWebServerFactory factory = mock(ConfigurableServletWebServerFactory.class);
this.properties.getServlet().setApplicationDisplayName("TestName");
this.customizer.customize(factory);
then(factory).should().setDisplayName("TestName");
}
@Test
void withNoCustomMimeMappingsThenEmptyMimeMappingsIsAdded() {
ConfigurableServletWebServerFactory factory = mock(ConfigurableServletWebServerFactory.class);
this.customizer.customize(factory);
ArgumentCaptor<MimeMappings> mimeMappingsCaptor = ArgumentCaptor.forClass(MimeMappings.class);
then(factory).should().addMimeMappings(mimeMappingsCaptor.capture());
MimeMappings mimeMappings = mimeMappingsCaptor.getValue();
assertThat(mimeMappings.getAll()).isEmpty();
}
@Test
void withCustomMimeMappingsThenPopulatedMimeMappingsIsAdded() {
this.properties.getMimeMappings().add("a", "alpha");
this.properties.getMimeMappings().add("b", "bravo");
ConfigurableServletWebServerFactory factory = mock(ConfigurableServletWebServerFactory.class);
this.customizer.customize(factory);
ArgumentCaptor<MimeMappings> mimeMappingsCaptor = ArgumentCaptor.forClass(MimeMappings.class);
then(factory).should().addMimeMappings(mimeMappingsCaptor.capture());
MimeMappings mimeMappings = mimeMappingsCaptor.getValue();
assertThat(mimeMappings.getAll()).hasSize(2);
}
@Test
void testCustomizeDefaultServlet() {
ConfigurableServletWebServerFactory factory = mock(ConfigurableServletWebServerFactory.class);
this.properties.getServlet().setRegisterDefaultServlet(false);
this.customizer.customize(factory);
then(factory).should().setRegisterDefaultServlet(false);
}
@Test
void testCustomizeSsl() {
ConfigurableServletWebServerFactory factory = mock(ConfigurableServletWebServerFactory.class);
Ssl ssl = mock(Ssl.class);
this.properties.setSsl(ssl);
this.customizer.customize(factory);
then(factory).should().setSsl(ssl);
}
@Test
void testCustomizeJsp() {
ConfigurableServletWebServerFactory factory = mock(ConfigurableServletWebServerFactory.class);
this.customizer.customize(factory);
then(factory).should().setJsp(any(Jsp.class));
}
@Test
void customizeSessionProperties() {
Map<String, String> 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.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);
then(factory).should().setSession(assertArg((session) -> {
assertThat(session.getTimeout()).hasSeconds(123);
Cookie cookie = session.getCookie();
assertThat(cookie.getName()).isEqualTo("testname");
assertThat(cookie.getDomain()).isEqualTo("testdomain");
assertThat(cookie.getPath()).isEqualTo("/testpath");
assertThat(cookie.getHttpOnly()).isTrue();
assertThat(cookie.getMaxAge()).hasSeconds(60);
}));
}
@Test
void testCustomizeTomcatPort() {
ConfigurableServletWebServerFactory factory = mock(ConfigurableServletWebServerFactory.class);
this.properties.setPort(8080);
this.customizer.customize(factory);
then(factory).should().setPort(8080);
}
@Test
void customizeServletDisplayName() {
Map<String, String> map = new HashMap<>();
map.put("server.servlet.application-display-name", "MyBootApp");
bindProperties(map);
ConfigurableServletWebServerFactory factory = mock(ConfigurableServletWebServerFactory.class);
this.customizer.customize(factory);
then(factory).should().setDisplayName("MyBootApp");
}
@Test
void sessionStoreDir() {
Map<String, String> map = new HashMap<>();
map.put("server.servlet.session.store-dir", "mydirectory");
bindProperties(map);
ConfigurableServletWebServerFactory factory = mock(ConfigurableServletWebServerFactory.class);
this.customizer.customize(factory);
then(factory).should()
.setSession(assertArg((session) -> assertThat(session.getStoreDir()).isEqualTo(new File("mydirectory"))));
}
@Test
void whenShutdownPropertyIsSetThenShutdownIsCustomized() {
Map<String, String> map = new HashMap<>();
map.put("server.shutdown", "immediate");
bindProperties(map);
ConfigurableServletWebServerFactory factory = mock(ConfigurableServletWebServerFactory.class);
this.customizer.customize(factory);
then(factory).should().setShutdown(assertArg((shutdown) -> assertThat(shutdown).isEqualTo(Shutdown.IMMEDIATE)));
}
@Test
void noLocaleCharsetMapping() {
ConfigurableServletWebServerFactory factory = mock(ConfigurableServletWebServerFactory.class);
this.customizer.customize(factory);
then(factory).should(never()).setLocaleCharsetMappings(anyMap());
}
@Test
void customLocaleCharsetMappings() {
Map<String, String> map = Map.of("server.servlet.encoding.mapping.en", "UTF-8",
"server.servlet.encoding.mapping.fr_FR", "UTF-8");
bindProperties(map);
ConfigurableServletWebServerFactory factory = mock(ConfigurableServletWebServerFactory.class);
this.customizer.customize(factory);
then(factory).should()
.setLocaleCharsetMappings((assertArg((mappings) -> assertThat(mappings).hasSize(2)
.containsEntry(Locale.ENGLISH, StandardCharsets.UTF_8)
.containsEntry(Locale.FRANCE, StandardCharsets.UTF_8))));
}
private void bindProperties(Map<String, String> map) {
ConfigurationPropertySource source = new MapConfigurationPropertySource(map);
new Binder(source).bind("server", Bindable.ofInstance(this.properties));
}
}