Move web server auto-configure classes into spring-boot-web-server
This commit is contained in:
committed by
Phillip Webb
parent
dee54a8b99
commit
4471c0e644
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user