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,390 @@
/*
* 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;
import java.net.InetAddress;
import java.nio.charset.Charset;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.boot.convert.DurationUnit;
import org.springframework.boot.web.server.Compression;
import org.springframework.boot.web.server.Cookie;
import org.springframework.boot.web.server.Http2;
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.servlet.Jsp;
import org.springframework.boot.web.server.servlet.Session;
import org.springframework.util.StringUtils;
import org.springframework.util.unit.DataSize;
/**
* {@link ConfigurationProperties @ConfigurationProperties} for a web server (e.g. port
* and path settings).
*
* @author Dave Syer
* @author Stephane Nicoll
* @author Andy Wilkinson
* @author Ivan Sopov
* @author Marcos Barbero
* @author Eddú Meléndez
* @author Quinten De Swaef
* @author Venil Noronha
* @author Aurélien Leboulanger
* @author Brian Clozel
* @author Olivier Lamy
* @author Chentao Qu
* @author Artsiom Yudovin
* @author Andrew McGhie
* @author Rafiullah Hamedy
* @author Dirk Deyne
* @author HaiTao Zhang
* @author Victor Mandujano
* @author Chris Bono
* @author Parviz Rozikov
* @author Florian Storz
* @author Michael Weidmann
* @author Lasse Wulff
* @since 1.0.0
*/
@ConfigurationProperties("server")
public class ServerProperties {
/**
* Server HTTP port.
*/
private Integer port;
/**
* Network address to which the server should bind.
*/
private InetAddress address;
@NestedConfigurationProperty
private final ErrorProperties error = new ErrorProperties();
/**
* Strategy for handling X-Forwarded-* headers.
*/
private ForwardHeadersStrategy forwardHeadersStrategy;
/**
* Value to use for the Server response header (if empty, no header is sent).
*/
private String serverHeader;
/**
* Maximum size of the HTTP request header. Refer to the documentation for your chosen
* embedded server for details of exactly how this limit is applied. For example,
* Netty applies the limit separately to each individual header in the request whereas
* Tomcat applies the limit to the combined size of the request line and all of the
* header names and values in the request.
*/
private DataSize maxHttpRequestHeaderSize = DataSize.ofKilobytes(8);
/**
* Type of shutdown that the server will support.
*/
private Shutdown shutdown = Shutdown.GRACEFUL;
@NestedConfigurationProperty
private Ssl ssl;
@NestedConfigurationProperty
private final Compression compression = new Compression();
/**
* Custom MIME mappings in addition to the default MIME mappings.
*/
private final MimeMappings mimeMappings = new MimeMappings();
@NestedConfigurationProperty
private final Http2 http2 = new Http2();
private final Servlet servlet = new Servlet();
private final Reactive reactive = new Reactive();
public Integer getPort() {
return this.port;
}
public void setPort(Integer port) {
this.port = port;
}
public InetAddress getAddress() {
return this.address;
}
public void setAddress(InetAddress address) {
this.address = address;
}
public String getServerHeader() {
return this.serverHeader;
}
public void setServerHeader(String serverHeader) {
this.serverHeader = serverHeader;
}
public DataSize getMaxHttpRequestHeaderSize() {
return this.maxHttpRequestHeaderSize;
}
public void setMaxHttpRequestHeaderSize(DataSize maxHttpRequestHeaderSize) {
this.maxHttpRequestHeaderSize = maxHttpRequestHeaderSize;
}
public Shutdown getShutdown() {
return this.shutdown;
}
public void setShutdown(Shutdown shutdown) {
this.shutdown = shutdown;
}
public ErrorProperties getError() {
return this.error;
}
public Ssl getSsl() {
return this.ssl;
}
public void setSsl(Ssl ssl) {
this.ssl = ssl;
}
public Compression getCompression() {
return this.compression;
}
public MimeMappings getMimeMappings() {
return this.mimeMappings;
}
public void setMimeMappings(Map<String, String> customMappings) {
customMappings.forEach(this.mimeMappings::add);
}
public Http2 getHttp2() {
return this.http2;
}
public Servlet getServlet() {
return this.servlet;
}
public Reactive getReactive() {
return this.reactive;
}
public ForwardHeadersStrategy getForwardHeadersStrategy() {
return this.forwardHeadersStrategy;
}
public void setForwardHeadersStrategy(ForwardHeadersStrategy forwardHeadersStrategy) {
this.forwardHeadersStrategy = forwardHeadersStrategy;
}
/**
* Servlet server properties.
*/
public static class Servlet {
/**
* Servlet context init parameters.
*/
private final Map<String, String> contextParameters = new HashMap<>();
/**
* Context path of the application.
*/
private String contextPath;
/**
* Display name of the application.
*/
private String applicationDisplayName = "application";
/**
* Whether to register the default Servlet with the container.
*/
private boolean registerDefaultServlet = false;
private final Encoding encoding = new Encoding();
@NestedConfigurationProperty
private final Jsp jsp = new Jsp();
@NestedConfigurationProperty
private final Session session = new Session();
public String getContextPath() {
return this.contextPath;
}
public void setContextPath(String contextPath) {
this.contextPath = cleanContextPath(contextPath);
}
private String cleanContextPath(String contextPath) {
String candidate = null;
if (StringUtils.hasLength(contextPath)) {
candidate = contextPath.strip();
}
if (StringUtils.hasText(candidate) && candidate.endsWith("/")) {
return candidate.substring(0, candidate.length() - 1);
}
return candidate;
}
public String getApplicationDisplayName() {
return this.applicationDisplayName;
}
public void setApplicationDisplayName(String displayName) {
this.applicationDisplayName = displayName;
}
public boolean isRegisterDefaultServlet() {
return this.registerDefaultServlet;
}
public void setRegisterDefaultServlet(boolean registerDefaultServlet) {
this.registerDefaultServlet = registerDefaultServlet;
}
public Map<String, String> getContextParameters() {
return this.contextParameters;
}
public Encoding getEncoding() {
return this.encoding;
}
public Jsp getJsp() {
return this.jsp;
}
public Session getSession() {
return this.session;
}
}
/**
* Reactive server properties.
*/
public static class Reactive {
private final Session session = new Session();
public Session getSession() {
return this.session;
}
public static class Session {
/**
* Session timeout. If a duration suffix is not specified, seconds will be
* used.
*/
@DurationUnit(ChronoUnit.SECONDS)
private Duration timeout = Duration.ofMinutes(30);
/**
* Maximum number of sessions that can be stored.
*/
private int maxSessions = 10000;
@NestedConfigurationProperty
private final Cookie cookie = new Cookie();
public Duration getTimeout() {
return this.timeout;
}
public void setTimeout(Duration timeout) {
this.timeout = timeout;
}
public int getMaxSessions() {
return this.maxSessions;
}
public void setMaxSessions(int maxSessions) {
this.maxSessions = maxSessions;
}
public Cookie getCookie() {
return this.cookie;
}
}
}
/**
* Strategies for supporting forward headers.
*/
public enum ForwardHeadersStrategy {
/**
* Use the underlying container's native support for forwarded headers.
*/
NATIVE,
/**
* Use Spring's support for handling forwarded headers.
*/
FRAMEWORK,
/**
* Ignore X-Forwarded-* headers.
*/
NONE
}
public static class Encoding {
/**
* Mapping of locale to charset for response encoding.
*/
private Map<Locale, Charset> mapping;
public Map<Locale, Charset> getMapping() {
return this.mapping;
}
public void setMapping(Map<Locale, Charset> mapping) {
this.mapping = mapping;
}
}
}

View File

@@ -0,0 +1,20 @@
/*
* 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.
*/
/**
* Classes related to the auto-configuration of a web server.
*/
package org.springframework.boot.web.server.autoconfigure;

View File

@@ -0,0 +1,101 @@
/*
* 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 org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.boot.web.server.WebServerFactoryCustomizerBeanPostProcessor;
import org.springframework.boot.web.server.autoconfigure.ServerProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.ObjectUtils;
import org.springframework.web.server.adapter.ForwardedHeaderTransformer;
/**
* {@link Configuration Configuration} for a reactive web server.
*
* @author Brian Clozel
* @author Scott Frederick
* @since 4.0.0
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(ServerProperties.class)
@Import(ReactiveWebServerConfiguration.BeanPostProcessorsRegistrar.class)
public class ReactiveWebServerConfiguration {
@Bean
public ReactiveWebServerFactoryCustomizer reactiveWebServerFactoryCustomizer(ServerProperties serverProperties,
ObjectProvider<SslBundles> sslBundles) {
return new ReactiveWebServerFactoryCustomizer(serverProperties, sslBundles.getIfAvailable());
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "server.forward-headers-strategy", havingValue = "framework")
public ForwardedHeaderTransformer forwardedHeaderTransformer() {
return new ForwardedHeaderTransformer();
}
/**
* Registers a {@link WebServerFactoryCustomizerBeanPostProcessor}. Registered via
* {@link ImportBeanDefinitionRegistrar} for early registration.
*/
public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
private ConfigurableListableBeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof ConfigurableListableBeanFactory listableBeanFactory) {
this.beanFactory = listableBeanFactory;
}
}
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
if (this.beanFactory == null) {
return;
}
registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
WebServerFactoryCustomizerBeanPostProcessor.class);
}
private <T> void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name,
Class<T> beanClass) {
if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
beanDefinition.setSynthetic(true);
registry.registerBeanDefinition(name, beanDefinition);
}
}
}
}

View File

@@ -0,0 +1,78 @@
/*
* 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 org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.server.autoconfigure.ServerProperties;
import org.springframework.boot.web.server.reactive.ConfigurableReactiveWebServerFactory;
import org.springframework.core.Ordered;
/**
* {@link WebServerFactoryCustomizer} to apply {@link ServerProperties} to reactive
* servers.
*
* @author Brian Clozel
* @author Yunkun Huang
* @author Scott Frederick
* @since 4.0.0
*/
public class ReactiveWebServerFactoryCustomizer
implements WebServerFactoryCustomizer<ConfigurableReactiveWebServerFactory>, Ordered {
private final ServerProperties serverProperties;
private final SslBundles sslBundles;
/**
* Create a new {@link ReactiveWebServerFactoryCustomizer} instance.
* @param serverProperties the server properties
*/
public ReactiveWebServerFactoryCustomizer(ServerProperties serverProperties) {
this(serverProperties, null);
}
/**
* Create a new {@link ReactiveWebServerFactoryCustomizer} instance.
* @param serverProperties the server properties
* @param sslBundles the SSL bundles
* @since 4.0.0
*/
public ReactiveWebServerFactoryCustomizer(ServerProperties serverProperties, SslBundles sslBundles) {
this.serverProperties = serverProperties;
this.sslBundles = sslBundles;
}
@Override
public int getOrder() {
return 0;
}
@Override
public void customize(ConfigurableReactiveWebServerFactory 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::getSsl).to(factory::setSsl);
map.from(this.serverProperties::getCompression).to(factory::setCompression);
map.from(this.serverProperties::getHttp2).to(factory::setHttp2);
map.from(this.serverProperties.getShutdown()).to(factory::setShutdown);
map.from(() -> this.sslBundles).to(factory::setSslBundles);
}
}

View File

@@ -0,0 +1,20 @@
/*
* 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.
*/
/**
* Classes related to the auto-configuration of a reactive web server.
*/
package org.springframework.boot.web.server.autoconfigure.reactive;

View File

@@ -0,0 +1,35 @@
/*
* 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 org.springframework.web.filter.ForwardedHeaderFilter;
/**
* Customizer for the auto-configured {@link ForwardedHeaderFilter}.
*
* @author Andy Wilkinson
* @since 4.0.0
*/
public interface ForwardedHeaderFilterCustomizer {
/**
* Customizes the given {@code filter}.
* @param filter the filter to customize
*/
void customize(ForwardedHeaderFilter filter);
}

View File

@@ -0,0 +1,122 @@
/*
* 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 jakarta.servlet.DispatcherType;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingFilterBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.boot.web.error.ErrorPageRegistrarBeanPostProcessor;
import org.springframework.boot.web.server.WebServerFactoryCustomizerBeanPostProcessor;
import org.springframework.boot.web.server.autoconfigure.ServerProperties;
import org.springframework.boot.web.server.servlet.CookieSameSiteSupplier;
import org.springframework.boot.web.server.servlet.WebListenerRegistrar;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.Ordered;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.ObjectUtils;
import org.springframework.web.filter.ForwardedHeaderFilter;
/**
* {@link Configuration Configuration} for a servlet web server.
*
* @author Phillip Webb
* @author Dave Syer
* @author Ivan Sopov
* @author Brian Clozel
* @author Stephane Nicoll
* @author Scott Frederick
* @since 4.0.0
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(ServerProperties.class)
@Import(ServletWebServerConfiguration.BeanPostProcessorsRegistrar.class)
public class ServletWebServerConfiguration {
@Bean
ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties,
ObjectProvider<WebListenerRegistrar> webListenerRegistrars,
ObjectProvider<CookieSameSiteSupplier> cookieSameSiteSuppliers, ObjectProvider<SslBundles> sslBundles) {
return new ServletWebServerFactoryCustomizer(serverProperties, webListenerRegistrars.orderedStream().toList(),
cookieSameSiteSuppliers.orderedStream().toList(), sslBundles.getIfAvailable());
}
@Bean
@ConditionalOnProperty(name = "server.forward-headers-strategy", havingValue = "framework")
@ConditionalOnMissingFilterBean(ForwardedHeaderFilter.class)
FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter(
ObjectProvider<ForwardedHeaderFilterCustomizer> customizerProvider) {
ForwardedHeaderFilter filter = new ForwardedHeaderFilter();
customizerProvider.ifAvailable((customizer) -> customizer.customize(filter));
FilterRegistrationBean<ForwardedHeaderFilter> registration = new FilterRegistrationBean<>(filter);
registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR);
registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
return registration;
}
/**
* Registers a {@link WebServerFactoryCustomizerBeanPostProcessor}. Registered via
* {@link ImportBeanDefinitionRegistrar} for early registration.
*/
static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
private ConfigurableListableBeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof ConfigurableListableBeanFactory listableBeanFactory) {
this.beanFactory = listableBeanFactory;
}
}
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
if (this.beanFactory == null) {
return;
}
registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
WebServerFactoryCustomizerBeanPostProcessor.class);
registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
ErrorPageRegistrarBeanPostProcessor.class);
}
private <T> void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name,
Class<T> beanClass) {
if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
beanDefinition.setSynthetic(true);
registry.registerBeanDefinition(name, beanDefinition);
}
}
}
}

View File

@@ -0,0 +1,98 @@
/*
* 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.util.Collections;
import java.util.List;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.boot.ssl.SslBundles;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.server.autoconfigure.ServerProperties;
import org.springframework.boot.web.server.servlet.ConfigurableServletWebServerFactory;
import org.springframework.boot.web.server.servlet.CookieSameSiteSupplier;
import org.springframework.boot.web.server.servlet.WebListenerRegistrar;
import org.springframework.core.Ordered;
import org.springframework.util.CollectionUtils;
/**
* {@link WebServerFactoryCustomizer} to apply {@link ServerProperties} and
* {@link WebListenerRegistrar WebListenerRegistrars} to servlet web servers.
*
* @author Brian Clozel
* @author Stephane Nicoll
* @author Olivier Lamy
* @author Yunkun Huang
* @author Scott Frederick
* @author Lasse Wulff
* @since 4.0.0
*/
public class ServletWebServerFactoryCustomizer
implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {
private final ServerProperties serverProperties;
private final List<WebListenerRegistrar> webListenerRegistrars;
private final List<CookieSameSiteSupplier> cookieSameSiteSuppliers;
private final SslBundles sslBundles;
public ServletWebServerFactoryCustomizer(ServerProperties serverProperties) {
this(serverProperties, Collections.emptyList(), Collections.emptyList(), null);
}
public ServletWebServerFactoryCustomizer(ServerProperties serverProperties,
List<WebListenerRegistrar> webListenerRegistrars, List<CookieSameSiteSupplier> cookieSameSiteSuppliers,
SslBundles sslBundles) {
this.serverProperties = serverProperties;
this.webListenerRegistrars = webListenerRegistrars;
this.cookieSameSiteSuppliers = cookieSameSiteSuppliers;
this.sslBundles = sslBundles;
}
@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()::isRegisterDefaultServlet).to(factory::setRegisterDefaultServlet);
map.from(this.serverProperties.getServlet()::getSession).to(factory::setSession);
map.from(this.serverProperties::getSsl).to(factory::setSsl);
map.from(this.serverProperties.getServlet()::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);
map.from(this.serverProperties.getShutdown()).to(factory::setShutdown);
map.from(() -> this.sslBundles).to(factory::setSslBundles);
map.from(() -> this.cookieSameSiteSuppliers)
.whenNot(CollectionUtils::isEmpty)
.to(factory::setCookieSameSiteSuppliers);
map.from(this.serverProperties::getMimeMappings).to(factory::addMimeMappings);
map.from(this.serverProperties.getServlet().getEncoding()::getMapping).to(factory::setLocaleCharsetMappings);
this.webListenerRegistrars.forEach((registrar) -> registrar.register(factory));
}
}

View File

@@ -0,0 +1,20 @@
/*
* 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.
*/
/**
* Classes related to the auto-configuration of a servlet web server.
*/
package org.springframework.boot.web.server.autoconfigure.servlet;

View File

@@ -0,0 +1,217 @@
{
"properties": [
{
"name": "server.connection-timeout",
"type": "java.time.Duration",
"deprecation": {
"reason": "Each server behaves differently. Use server specific properties instead.",
"level": "error"
}
},
{
"name": "server.error.include-binding-errors",
"description": "When to include \"errors\" attribute.",
"defaultValue": "never"
},
{
"name": "server.error.include-exception",
"description": "Include the \"exception\" attribute.",
"defaultValue": false
},
{
"name": "server.error.include-message",
"description": "When to include the \"message\" attribute.",
"defaultValue": "never"
},
{
"name": "server.error.include-path",
"description": "When to include the \"path\" attribute.",
"defaultValue": "always"
},
{
"name": "server.error.include-stacktrace",
"description": "When to include the \"trace\" attribute.",
"defaultValue": "never"
},
{
"name": "server.error.path",
"description": "Path of the error controller",
"defaultValue": "/error"
},
{
"name": "server.error.whitelabel.enabled",
"description": "Whether to enable the default error page displayed in browsers in case of a server error.",
"defaultValue": true
},
{
"name": "server.max-http-header-size",
"deprecation": {
"replacement": "server.max-http-request-header-size",
"level": "error"
}
},
{
"name": "server.max-http-post-size",
"type": "java.lang.Integer",
"description": "Maximum size in bytes of the HTTP post content.",
"defaultValue": 0,
"deprecation": {
"reason": "Use dedicated property for each container.",
"level": "error"
}
},
{
"name": "server.port",
"defaultValue": 8080
},
{
"name": "server.reactive.session.cookie.domain",
"description": "Domain for the cookie."
},
{
"name": "server.reactive.session.cookie.http-only",
"description": "Whether to use \"HttpOnly\" cookies for the cookie."
},
{
"name": "server.reactive.session.cookie.max-age",
"description": "Maximum age of the cookie. If a duration suffix is not specified, seconds will be used. A positive value indicates when the cookie expires relative to the current time. A value of 0 means the cookie should expire immediately. A negative value means no \"Max-Age\"."
},
{
"name": "server.reactive.session.cookie.name",
"description": "Name for the cookie."
},
{
"name": "server.reactive.session.cookie.partitioned",
"description": "Whether the generated cookie carries the Partitioned attribute."
},
{
"name": "server.reactive.session.cookie.path",
"description": "Path of the cookie."
},
{
"name": "server.reactive.session.cookie.same-site",
"description": "SameSite setting for the cookie."
},
{
"name": "server.reactive.session.cookie.secure",
"description": "Whether to always mark the cookie as secure."
},
{
"name": "server.servlet.encoding.charset",
"type": "java.nio.charset.Charset",
"description": "Charset of HTTP requests and responses. Added to the Content-Type header if not set explicitly.",
"deprecation": {
"replacement": "spring.servlet.encoding.charset",
"level": "error"
}
},
{
"name": "server.servlet.encoding.enabled",
"type": "java.lang.Boolean",
"description": "Whether to enable http encoding support.",
"defaultValue": true,
"deprecation": {
"replacement": "spring.servlet.encoding.enabled",
"level": "error"
}
},
{
"name": "server.servlet.encoding.force",
"type": "java.lang.Boolean",
"description": "Whether to force the encoding to the configured charset on HTTP requests and responses.",
"defaultValue": false,
"deprecation": {
"replacement": "spring.servlet.encoding.force",
"level": "error"
}
},
{
"name": "server.servlet.encoding.force-request",
"type": "java.lang.Boolean",
"description": "Whether to force the encoding to the configured charset on HTTP requests. Defaults to true when force has not been specified.",
"defaultValue": true,
"deprecation": {
"replacement": "spring.servlet.encoding.force-request",
"level": "error"
}
},
{
"name": "server.servlet.encoding.force-response",
"type": "java.lang.Boolean",
"description": "Whether to force the encoding to the configured charset on HTTP responses.",
"defaultValue": false,
"deprecation": {
"replacement": "spring.servlet.encoding.force-response",
"level": "error"
}
},
{
"name": "server.servlet.jsp.class-name",
"description": "Class name of the servlet to use for JSPs. If registered is true and this class\n\t * is on the classpath then it will be registered.",
"defaultValue": "org.apache.jasper.servlet.JspServlet"
},
{
"name": "server.servlet.jsp.init-parameters",
"description": "Init parameters used to configure the JSP servlet."
},
{
"name": "server.servlet.path",
"type": "java.lang.String",
"description": "Path of the main dispatcher servlet.",
"defaultValue": "/",
"deprecation": {
"replacement": "spring.mvc.servlet.path",
"level": "error"
}
},
{
"name": "server.servlet.session.cookie.comment",
"description": "Comment for the cookie.",
"deprecation": {
"level": "error"
}
},
{
"name": "server.ssl.protocol",
"description": "SSL protocol to use.",
"defaultValue": "TLS"
},
{
"name": "server.ssl.server-name-bundles",
"description": "Mapping of host names to SSL bundles for SNI configuration."
},
{
"name": "server.ssl.trust-certificate",
"description": "Path to a PEM-encoded SSL certificate authority file."
},
{
"name": "server.ssl.trust-certificate-private-key",
"description": "Path to a PEM-encoded private key file for the SSL certificate authority."
},
{
"name": "server.ssl.trust-store",
"description": "Trust store that holds SSL certificates."
},
{
"name": "server.ssl.trust-store-password",
"description": "Password used to access the trust store."
},
{
"name": "server.ssl.trust-store-provider",
"description": "Provider for the trust store."
},
{
"name": "server.ssl.trust-store-type",
"description": "Type of the trust store."
},
{
"name": "server.use-forward-headers",
"type": "java.lang.Boolean",
"deprecation": {
"reason": "Replaced to support additional strategies.",
"replacement": "server.forward-headers-strategy",
"level": "error"
}
}
]
}