Move server-specific properties out of ServerProperties
Issue: 44064
This commit is contained in:
parent
ebd4c1a326
commit
90dc10cdee
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,359 @@
|
|||
/*
|
||||
* 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.server.jetty;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.util.unit.DataSize;
|
||||
|
||||
/**
|
||||
* Jetty server properties.
|
||||
*
|
||||
* @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 3.5.0
|
||||
*/
|
||||
@ConfigurationProperties("server.jetty")
|
||||
public class JettyServerProperties {
|
||||
|
||||
/**
|
||||
* Maximum size of the form content in any HTTP post request.
|
||||
*/
|
||||
private DataSize maxHttpFormPostSize = DataSize.ofBytes(200000);
|
||||
|
||||
/**
|
||||
* Maximum number of form keys.
|
||||
*/
|
||||
private int maxFormKeys = 1000;
|
||||
|
||||
/**
|
||||
* Time that the connection can be idle before it is closed.
|
||||
*/
|
||||
private Duration connectionIdleTimeout;
|
||||
|
||||
/**
|
||||
* Maximum size of the HTTP response header.
|
||||
*/
|
||||
private DataSize maxHttpResponseHeaderSize = DataSize.ofKilobytes(8);
|
||||
|
||||
/**
|
||||
* Maximum number of connections that the server accepts and processes at any given
|
||||
* time.
|
||||
*/
|
||||
private int maxConnections = -1;
|
||||
|
||||
/**
|
||||
* Access log configuration.
|
||||
*/
|
||||
private final Accesslog accesslog = new Accesslog();
|
||||
|
||||
/**
|
||||
* Thread related configuration.
|
||||
*/
|
||||
private final Threads threads = new Threads();
|
||||
|
||||
public Accesslog getAccesslog() {
|
||||
return this.accesslog;
|
||||
}
|
||||
|
||||
public Threads getThreads() {
|
||||
return this.threads;
|
||||
}
|
||||
|
||||
public DataSize getMaxHttpFormPostSize() {
|
||||
return this.maxHttpFormPostSize;
|
||||
}
|
||||
|
||||
public void setMaxHttpFormPostSize(DataSize maxHttpFormPostSize) {
|
||||
this.maxHttpFormPostSize = maxHttpFormPostSize;
|
||||
}
|
||||
|
||||
public int getMaxFormKeys() {
|
||||
return this.maxFormKeys;
|
||||
}
|
||||
|
||||
public void setMaxFormKeys(int maxFormKeys) {
|
||||
this.maxFormKeys = maxFormKeys;
|
||||
}
|
||||
|
||||
public Duration getConnectionIdleTimeout() {
|
||||
return this.connectionIdleTimeout;
|
||||
}
|
||||
|
||||
public void setConnectionIdleTimeout(Duration connectionIdleTimeout) {
|
||||
this.connectionIdleTimeout = connectionIdleTimeout;
|
||||
}
|
||||
|
||||
public DataSize getMaxHttpResponseHeaderSize() {
|
||||
return this.maxHttpResponseHeaderSize;
|
||||
}
|
||||
|
||||
public void setMaxHttpResponseHeaderSize(DataSize maxHttpResponseHeaderSize) {
|
||||
this.maxHttpResponseHeaderSize = maxHttpResponseHeaderSize;
|
||||
}
|
||||
|
||||
public int getMaxConnections() {
|
||||
return this.maxConnections;
|
||||
}
|
||||
|
||||
public void setMaxConnections(int maxConnections) {
|
||||
this.maxConnections = maxConnections;
|
||||
}
|
||||
|
||||
/**
|
||||
* Jetty access log properties.
|
||||
*/
|
||||
public static class Accesslog {
|
||||
|
||||
/**
|
||||
* Enable access log.
|
||||
*/
|
||||
private boolean enabled = false;
|
||||
|
||||
/**
|
||||
* Log format.
|
||||
*/
|
||||
private Accesslog.FORMAT format = FORMAT.NCSA;
|
||||
|
||||
/**
|
||||
* Custom log format, see org.eclipse.jetty.server.CustomRequestLog. If defined,
|
||||
* overrides the "format" configuration key.
|
||||
*/
|
||||
private String customFormat;
|
||||
|
||||
/**
|
||||
* Log filename. If not specified, logs redirect to "System.err".
|
||||
*/
|
||||
private String filename;
|
||||
|
||||
/**
|
||||
* Date format to place in log file name.
|
||||
*/
|
||||
private String fileDateFormat;
|
||||
|
||||
/**
|
||||
* Number of days before rotated log files are deleted.
|
||||
*/
|
||||
private int retentionPeriod = 31; // no days
|
||||
|
||||
/**
|
||||
* Append to log.
|
||||
*/
|
||||
private boolean append;
|
||||
|
||||
/**
|
||||
* Request paths that should not be logged.
|
||||
*/
|
||||
private List<String> ignorePaths;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public Accesslog.FORMAT getFormat() {
|
||||
return this.format;
|
||||
}
|
||||
|
||||
public void setFormat(Accesslog.FORMAT format) {
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
public String getCustomFormat() {
|
||||
return this.customFormat;
|
||||
}
|
||||
|
||||
public void setCustomFormat(String customFormat) {
|
||||
this.customFormat = customFormat;
|
||||
}
|
||||
|
||||
public String getFilename() {
|
||||
return this.filename;
|
||||
}
|
||||
|
||||
public void setFilename(String filename) {
|
||||
this.filename = filename;
|
||||
}
|
||||
|
||||
public String getFileDateFormat() {
|
||||
return this.fileDateFormat;
|
||||
}
|
||||
|
||||
public void setFileDateFormat(String fileDateFormat) {
|
||||
this.fileDateFormat = fileDateFormat;
|
||||
}
|
||||
|
||||
public int getRetentionPeriod() {
|
||||
return this.retentionPeriod;
|
||||
}
|
||||
|
||||
public void setRetentionPeriod(int retentionPeriod) {
|
||||
this.retentionPeriod = retentionPeriod;
|
||||
}
|
||||
|
||||
public boolean isAppend() {
|
||||
return this.append;
|
||||
}
|
||||
|
||||
public void setAppend(boolean append) {
|
||||
this.append = append;
|
||||
}
|
||||
|
||||
public List<String> getIgnorePaths() {
|
||||
return this.ignorePaths;
|
||||
}
|
||||
|
||||
public void setIgnorePaths(List<String> ignorePaths) {
|
||||
this.ignorePaths = ignorePaths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log format for Jetty access logs.
|
||||
*/
|
||||
public enum FORMAT {
|
||||
|
||||
/**
|
||||
* NCSA format, as defined in CustomRequestLog#NCSA_FORMAT.
|
||||
*/
|
||||
NCSA,
|
||||
|
||||
/**
|
||||
* Extended NCSA format, as defined in CustomRequestLog#EXTENDED_NCSA_FORMAT.
|
||||
*/
|
||||
EXTENDED_NCSA
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Jetty thread properties.
|
||||
*/
|
||||
public static class Threads {
|
||||
|
||||
/**
|
||||
* Number of acceptor threads to use. When the value is -1, the default, the
|
||||
* number of acceptors is derived from the operating environment.
|
||||
*/
|
||||
private Integer acceptors = -1;
|
||||
|
||||
/**
|
||||
* Number of selector threads to use. When the value is -1, the default, the
|
||||
* number of selectors is derived from the operating environment.
|
||||
*/
|
||||
private Integer selectors = -1;
|
||||
|
||||
/**
|
||||
* Maximum number of threads. Doesn't have an effect if virtual threads are
|
||||
* enabled.
|
||||
*/
|
||||
private Integer max = 200;
|
||||
|
||||
/**
|
||||
* Minimum number of threads. Doesn't have an effect if virtual threads are
|
||||
* enabled.
|
||||
*/
|
||||
private Integer min = 8;
|
||||
|
||||
/**
|
||||
* Maximum capacity of the thread pool's backing queue. A default is computed
|
||||
* based on the threading configuration.
|
||||
*/
|
||||
private Integer maxQueueCapacity;
|
||||
|
||||
/**
|
||||
* Maximum thread idle time.
|
||||
*/
|
||||
private Duration idleTimeout = Duration.ofMillis(60000);
|
||||
|
||||
public Integer getAcceptors() {
|
||||
return this.acceptors;
|
||||
}
|
||||
|
||||
public void setAcceptors(Integer acceptors) {
|
||||
this.acceptors = acceptors;
|
||||
}
|
||||
|
||||
public Integer getSelectors() {
|
||||
return this.selectors;
|
||||
}
|
||||
|
||||
public void setSelectors(Integer selectors) {
|
||||
this.selectors = selectors;
|
||||
}
|
||||
|
||||
public void setMin(Integer min) {
|
||||
this.min = min;
|
||||
}
|
||||
|
||||
public Integer getMin() {
|
||||
return this.min;
|
||||
}
|
||||
|
||||
public void setMax(Integer max) {
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
public Integer getMax() {
|
||||
return this.max;
|
||||
}
|
||||
|
||||
public Integer getMaxQueueCapacity() {
|
||||
return this.maxQueueCapacity;
|
||||
}
|
||||
|
||||
public void setMaxQueueCapacity(Integer maxQueueCapacity) {
|
||||
this.maxQueueCapacity = maxQueueCapacity;
|
||||
}
|
||||
|
||||
public void setIdleTimeout(Duration idleTimeout) {
|
||||
this.idleTimeout = idleTimeout;
|
||||
}
|
||||
|
||||
public Duration getIdleTimeout() {
|
||||
return this.idleTimeout;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -23,11 +23,9 @@ import org.eclipse.jetty.util.BlockingArrayQueue;
|
|||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.eclipse.jetty.util.thread.ThreadPool;
|
||||
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
|
||||
/**
|
||||
* Creates a {@link ThreadPool} for Jetty, applying
|
||||
* {@link org.springframework.boot.autoconfigure.web.ServerProperties.Jetty.Threads
|
||||
* {@link org.springframework.boot.autoconfigure.web.server.jetty.JettyServerProperties.Threads
|
||||
* ServerProperties.Jetty.Threads Jetty thread properties}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
|
@ -37,7 +35,7 @@ final class JettyThreadPool {
|
|||
private JettyThreadPool() {
|
||||
}
|
||||
|
||||
static QueuedThreadPool create(ServerProperties.Jetty.Threads properties) {
|
||||
static QueuedThreadPool create(JettyServerProperties.Threads properties) {
|
||||
BlockingQueue<Runnable> queue = determineBlockingQueue(properties.getMaxQueueCapacity());
|
||||
int maxThreadCount = (properties.getMax() > 0) ? properties.getMax() : 200;
|
||||
int minThreadCount = (properties.getMin() > 0) ? properties.getMin() : 8;
|
||||
|
|
|
@ -19,10 +19,13 @@ package org.springframework.boot.autoconfigure.web.server.jetty;
|
|||
import org.eclipse.jetty.util.VirtualThreads;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.context.properties.bind.Bindable;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||
import org.springframework.boot.web.server.jetty.ConfigurableJettyWebServerFactory;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
|
@ -32,18 +35,21 @@ import org.springframework.util.Assert;
|
|||
* @since 4.0.0
|
||||
*/
|
||||
public class JettyVirtualThreadsWebServerFactoryCustomizer
|
||||
implements WebServerFactoryCustomizer<ConfigurableJettyWebServerFactory>, Ordered {
|
||||
implements WebServerFactoryCustomizer<ConfigurableJettyWebServerFactory>, Ordered, EnvironmentAware {
|
||||
|
||||
private final ServerProperties serverProperties;
|
||||
private final JettyServerProperties jettyProperties;
|
||||
|
||||
public JettyVirtualThreadsWebServerFactoryCustomizer(ServerProperties serverProperties) {
|
||||
this.serverProperties = serverProperties;
|
||||
private final boolean bind;
|
||||
|
||||
public JettyVirtualThreadsWebServerFactoryCustomizer(JettyServerProperties jettyProperties) {
|
||||
this.jettyProperties = jettyProperties;
|
||||
this.bind = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(ConfigurableJettyWebServerFactory factory) {
|
||||
Assert.state(VirtualThreads.areSupported(), "Virtual threads are not supported");
|
||||
QueuedThreadPool threadPool = JettyThreadPool.create(this.serverProperties.getJetty().getThreads());
|
||||
QueuedThreadPool threadPool = JettyThreadPool.create(this.jettyProperties.getThreads());
|
||||
threadPool.setVirtualThreadsExecutor(VirtualThreads.getNamedVirtualThreadsExecutor("jetty-"));
|
||||
factory.setThreadPool(threadPool);
|
||||
}
|
||||
|
@ -53,4 +59,11 @@ public class JettyVirtualThreadsWebServerFactoryCustomizer
|
|||
return JettyWebServerFactoryCustomizer.ORDER + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnvironment(Environment environment) {
|
||||
if (this.bind) {
|
||||
Binder.get(environment).bind("server.jetty", Bindable.ofInstance(this.jettyProperties));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -38,17 +38,22 @@ import org.springframework.core.env.Environment;
|
|||
@Configuration(proxyBeanMethods = false)
|
||||
public class JettyWebServerConfiguration {
|
||||
|
||||
private final JettyServerProperties jettyProperties;
|
||||
|
||||
public JettyWebServerConfiguration(JettyServerProperties jettyProperties) {
|
||||
this.jettyProperties = jettyProperties;
|
||||
}
|
||||
|
||||
@Bean
|
||||
JettyWebServerFactoryCustomizer jettyWebServerFactoryCustomizer(Environment environment,
|
||||
ServerProperties serverProperties) {
|
||||
return new JettyWebServerFactoryCustomizer(environment, serverProperties);
|
||||
return new JettyWebServerFactoryCustomizer(environment, serverProperties, this.jettyProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnThreading(Threading.VIRTUAL)
|
||||
JettyVirtualThreadsWebServerFactoryCustomizer jettyVirtualThreadsWebServerFactoryCustomizer(
|
||||
ServerProperties serverProperties) {
|
||||
return new JettyVirtualThreadsWebServerFactoryCustomizer(serverProperties);
|
||||
JettyVirtualThreadsWebServerFactoryCustomizer jettyVirtualThreadsWebServerFactoryCustomizer() {
|
||||
return new JettyVirtualThreadsWebServerFactoryCustomizer(this.jettyProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -62,9 +62,13 @@ public class JettyWebServerFactoryCustomizer
|
|||
|
||||
private final ServerProperties serverProperties;
|
||||
|
||||
public JettyWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {
|
||||
private final JettyServerProperties jettyProperties;
|
||||
|
||||
public JettyWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties,
|
||||
JettyServerProperties jettyProperties) {
|
||||
this.environment = environment;
|
||||
this.serverProperties = serverProperties;
|
||||
this.jettyProperties = jettyProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -74,34 +78,33 @@ public class JettyWebServerFactoryCustomizer
|
|||
|
||||
@Override
|
||||
public void customize(ConfigurableJettyWebServerFactory factory) {
|
||||
ServerProperties.Jetty properties = this.serverProperties.getJetty();
|
||||
factory.setUseForwardHeaders(getOrDeduceUseForwardHeaders());
|
||||
ServerProperties.Jetty.Threads threadProperties = properties.getThreads();
|
||||
factory.setThreadPool(JettyThreadPool.create(properties.getThreads()));
|
||||
JettyServerProperties.Threads threadProperties = this.jettyProperties.getThreads();
|
||||
factory.setThreadPool(JettyThreadPool.create(this.jettyProperties.getThreads()));
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
map.from(properties::getMaxConnections).to(factory::setMaxConnections);
|
||||
map.from(this.jettyProperties::getMaxConnections).to(factory::setMaxConnections);
|
||||
map.from(threadProperties::getAcceptors).to(factory::setAcceptors);
|
||||
map.from(threadProperties::getSelectors).to(factory::setSelectors);
|
||||
map.from(this.serverProperties::getMaxHttpRequestHeaderSize)
|
||||
.asInt(DataSize::toBytes)
|
||||
.when(this::isPositive)
|
||||
.to(customizeHttpConfigurations(factory, HttpConfiguration::setRequestHeaderSize));
|
||||
map.from(properties::getMaxHttpResponseHeaderSize)
|
||||
map.from(this.jettyProperties::getMaxHttpResponseHeaderSize)
|
||||
.asInt(DataSize::toBytes)
|
||||
.when(this::isPositive)
|
||||
.to(customizeHttpConfigurations(factory, HttpConfiguration::setResponseHeaderSize));
|
||||
map.from(properties::getMaxHttpFormPostSize)
|
||||
map.from(this.jettyProperties::getMaxHttpFormPostSize)
|
||||
.asInt(DataSize::toBytes)
|
||||
.when(this::isPositive)
|
||||
.to(customizeServletContextHandler(factory, ServletContextHandler::setMaxFormContentSize));
|
||||
map.from(properties::getMaxFormKeys)
|
||||
map.from(this.jettyProperties::getMaxFormKeys)
|
||||
.when(this::isPositive)
|
||||
.to(customizeServletContextHandler(factory, ServletContextHandler::setMaxFormKeys));
|
||||
map.from(properties::getConnectionIdleTimeout)
|
||||
map.from(this.jettyProperties::getConnectionIdleTimeout)
|
||||
.as(Duration::toMillis)
|
||||
.to(customizeAbstractConnectors(factory, AbstractConnector::setIdleTimeout));
|
||||
map.from(properties::getAccesslog)
|
||||
.when(ServerProperties.Jetty.Accesslog::isEnabled)
|
||||
map.from(this.jettyProperties::getAccesslog)
|
||||
.when(JettyServerProperties.Accesslog::isEnabled)
|
||||
.to((accesslog) -> customizeAccessLog(factory, accesslog));
|
||||
}
|
||||
|
||||
|
@ -177,7 +180,7 @@ public class JettyWebServerFactoryCustomizer
|
|||
}
|
||||
|
||||
private void customizeAccessLog(ConfigurableJettyWebServerFactory factory,
|
||||
ServerProperties.Jetty.Accesslog properties) {
|
||||
JettyServerProperties.Accesslog properties) {
|
||||
factory.addServerCustomizers((server) -> {
|
||||
RequestLogWriter logWriter = new RequestLogWriter();
|
||||
String format = getLogFormat(properties);
|
||||
|
@ -197,11 +200,11 @@ public class JettyWebServerFactoryCustomizer
|
|||
});
|
||||
}
|
||||
|
||||
private String getLogFormat(ServerProperties.Jetty.Accesslog properties) {
|
||||
private String getLogFormat(JettyServerProperties.Accesslog properties) {
|
||||
if (properties.getCustomFormat() != null) {
|
||||
return properties.getCustomFormat();
|
||||
}
|
||||
if (ServerProperties.Jetty.Accesslog.FORMAT.EXTENDED_NCSA.equals(properties.getFormat())) {
|
||||
if (JettyServerProperties.Accesslog.FORMAT.EXTENDED_NCSA.equals(properties.getFormat())) {
|
||||
return CustomRequestLog.EXTENDED_NCSA_FORMAT;
|
||||
}
|
||||
return CustomRequestLog.NCSA_FORMAT;
|
||||
|
|
|
@ -26,8 +26,10 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.web.server.jetty.JettyServerProperties;
|
||||
import org.springframework.boot.autoconfigure.web.server.jetty.JettyWebServerConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.server.reactive.ReactiveWebServerConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.server.jetty.JettyServerCustomizer;
|
||||
import org.springframework.boot.web.server.reactive.ReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.server.reactive.jetty.JettyReactiveWebServerFactory;
|
||||
|
@ -46,6 +48,7 @@ import org.springframework.http.ReactiveHttpInputMessage;
|
|||
@AutoConfiguration
|
||||
@ConditionalOnClass({ Server.class, ServletHolder.class, ReactiveHttpInputMessage.class })
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
|
||||
@EnableConfigurationProperties(JettyServerProperties.class)
|
||||
@Import({ JettyWebServerConfiguration.class, ReactiveWebServerConfiguration.class })
|
||||
public class JettyReactiveWebServerAutoConfiguration {
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
|
|||
import org.springframework.boot.autoconfigure.reactor.netty.ReactorNettyConfigurations.ReactorResourceFactoryConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.autoconfigure.web.server.reactive.ReactiveWebServerConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.server.reactive.ReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.server.reactive.netty.NettyReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.server.reactive.netty.NettyRouteProvider;
|
||||
|
@ -45,6 +46,7 @@ import org.springframework.http.client.ReactorResourceFactory;
|
|||
*/
|
||||
@AutoConfiguration
|
||||
@ConditionalOnClass({ ReactiveHttpInputMessage.class, HttpServer.class })
|
||||
@EnableConfigurationProperties(NettyServerProperties.class)
|
||||
@Import({ ReactiveWebServerConfiguration.class, ReactorResourceFactoryConfiguration.class })
|
||||
public class NettyReactiveWebServerAutoConfiguration {
|
||||
|
||||
|
@ -61,8 +63,8 @@ public class NettyReactiveWebServerAutoConfiguration {
|
|||
|
||||
@Bean
|
||||
NettyReactiveWebServerFactoryCustomizer nettyWebServerFactoryCustomizer(Environment environment,
|
||||
ServerProperties serverProperties) {
|
||||
return new NettyReactiveWebServerFactoryCustomizer(environment, serverProperties);
|
||||
ServerProperties serverProperties, NettyServerProperties nettyProperties) {
|
||||
return new NettyReactiveWebServerFactoryCustomizer(environment, serverProperties, nettyProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -43,9 +43,13 @@ public class NettyReactiveWebServerFactoryCustomizer
|
|||
|
||||
private final ServerProperties serverProperties;
|
||||
|
||||
public NettyReactiveWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {
|
||||
private final NettyServerProperties nettyProperties;
|
||||
|
||||
public NettyReactiveWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties,
|
||||
NettyServerProperties nettyProperties) {
|
||||
this.environment = environment;
|
||||
this.serverProperties = serverProperties;
|
||||
this.nettyProperties = nettyProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -57,11 +61,10 @@ public class NettyReactiveWebServerFactoryCustomizer
|
|||
public void customize(NettyReactiveWebServerFactory factory) {
|
||||
factory.setUseForwardHeaders(getOrDeduceUseForwardHeaders());
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
ServerProperties.Netty nettyProperties = this.serverProperties.getNetty();
|
||||
map.from(nettyProperties::getConnectionTimeout)
|
||||
map.from(this.nettyProperties::getConnectionTimeout)
|
||||
.to((connectionTimeout) -> customizeConnectionTimeout(factory, connectionTimeout));
|
||||
map.from(nettyProperties::getIdleTimeout).to((idleTimeout) -> customizeIdleTimeout(factory, idleTimeout));
|
||||
map.from(nettyProperties::getMaxKeepAliveRequests)
|
||||
map.from(this.nettyProperties::getIdleTimeout).to((idleTimeout) -> customizeIdleTimeout(factory, idleTimeout));
|
||||
map.from(this.nettyProperties::getMaxKeepAliveRequests)
|
||||
.to((maxKeepAliveRequests) -> customizeMaxKeepAliveRequests(factory, maxKeepAliveRequests));
|
||||
if (this.serverProperties.getHttp2() != null && this.serverProperties.getHttp2().isEnabled()) {
|
||||
map.from(this.serverProperties.getMaxHttpRequestHeaderSize())
|
||||
|
@ -88,16 +91,15 @@ public class NettyReactiveWebServerFactoryCustomizer
|
|||
propertyMapper.from(this.serverProperties.getMaxHttpRequestHeaderSize())
|
||||
.to((maxHttpRequestHeader) -> httpRequestDecoderSpec
|
||||
.maxHeaderSize((int) maxHttpRequestHeader.toBytes()));
|
||||
ServerProperties.Netty nettyProperties = this.serverProperties.getNetty();
|
||||
propertyMapper.from(nettyProperties.getMaxInitialLineLength())
|
||||
propertyMapper.from(this.nettyProperties.getMaxInitialLineLength())
|
||||
.to((maxInitialLineLength) -> httpRequestDecoderSpec
|
||||
.maxInitialLineLength((int) maxInitialLineLength.toBytes()));
|
||||
propertyMapper.from(nettyProperties.getH2cMaxContentLength())
|
||||
propertyMapper.from(this.nettyProperties.getH2cMaxContentLength())
|
||||
.to((h2cMaxContentLength) -> httpRequestDecoderSpec
|
||||
.h2cMaxContentLength((int) h2cMaxContentLength.toBytes()));
|
||||
propertyMapper.from(nettyProperties.getInitialBufferSize())
|
||||
propertyMapper.from(this.nettyProperties.getInitialBufferSize())
|
||||
.to((initialBufferSize) -> httpRequestDecoderSpec.initialBufferSize((int) initialBufferSize.toBytes()));
|
||||
propertyMapper.from(nettyProperties.isValidateHeaders()).to(httpRequestDecoderSpec::validateHeaders);
|
||||
propertyMapper.from(this.nettyProperties.isValidateHeaders()).to(httpRequestDecoderSpec::validateHeaders);
|
||||
return httpRequestDecoderSpec;
|
||||
}));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* 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.server.reactive.netty;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.util.unit.DataSize;
|
||||
|
||||
/**
|
||||
* Netty server properties.
|
||||
*
|
||||
* @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 3.5.0
|
||||
*/
|
||||
@ConfigurationProperties("server.netty")
|
||||
public class NettyServerProperties {
|
||||
|
||||
/**
|
||||
* Connection timeout of the Netty channel.
|
||||
*/
|
||||
private Duration connectionTimeout;
|
||||
|
||||
/**
|
||||
* Maximum content length of an H2C upgrade request.
|
||||
*/
|
||||
private DataSize h2cMaxContentLength = DataSize.ofBytes(0);
|
||||
|
||||
/**
|
||||
* Initial buffer size for HTTP request decoding.
|
||||
*/
|
||||
private DataSize initialBufferSize = DataSize.ofBytes(128);
|
||||
|
||||
/**
|
||||
* Maximum length that can be decoded for an HTTP request's initial line.
|
||||
*/
|
||||
private DataSize maxInitialLineLength = DataSize.ofKilobytes(4);
|
||||
|
||||
/**
|
||||
* Maximum number of requests that can be made per connection. By default, a
|
||||
* connection serves unlimited number of requests.
|
||||
*/
|
||||
private Integer maxKeepAliveRequests;
|
||||
|
||||
/**
|
||||
* Whether to validate headers when decoding requests.
|
||||
*/
|
||||
private boolean validateHeaders = true;
|
||||
|
||||
/**
|
||||
* Idle timeout of the Netty channel. When not specified, an infinite timeout is used.
|
||||
*/
|
||||
private Duration idleTimeout;
|
||||
|
||||
public Duration getConnectionTimeout() {
|
||||
return this.connectionTimeout;
|
||||
}
|
||||
|
||||
public void setConnectionTimeout(Duration connectionTimeout) {
|
||||
this.connectionTimeout = connectionTimeout;
|
||||
}
|
||||
|
||||
public DataSize getH2cMaxContentLength() {
|
||||
return this.h2cMaxContentLength;
|
||||
}
|
||||
|
||||
public void setH2cMaxContentLength(DataSize h2cMaxContentLength) {
|
||||
this.h2cMaxContentLength = h2cMaxContentLength;
|
||||
}
|
||||
|
||||
public DataSize getInitialBufferSize() {
|
||||
return this.initialBufferSize;
|
||||
}
|
||||
|
||||
public void setInitialBufferSize(DataSize initialBufferSize) {
|
||||
this.initialBufferSize = initialBufferSize;
|
||||
}
|
||||
|
||||
public DataSize getMaxInitialLineLength() {
|
||||
return this.maxInitialLineLength;
|
||||
}
|
||||
|
||||
public void setMaxInitialLineLength(DataSize maxInitialLineLength) {
|
||||
this.maxInitialLineLength = maxInitialLineLength;
|
||||
}
|
||||
|
||||
public Integer getMaxKeepAliveRequests() {
|
||||
return this.maxKeepAliveRequests;
|
||||
}
|
||||
|
||||
public void setMaxKeepAliveRequests(Integer maxKeepAliveRequests) {
|
||||
this.maxKeepAliveRequests = maxKeepAliveRequests;
|
||||
}
|
||||
|
||||
public boolean isValidateHeaders() {
|
||||
return this.validateHeaders;
|
||||
}
|
||||
|
||||
public void setValidateHeaders(boolean validateHeaders) {
|
||||
this.validateHeaders = validateHeaders;
|
||||
}
|
||||
|
||||
public Duration getIdleTimeout() {
|
||||
return this.idleTimeout;
|
||||
}
|
||||
|
||||
public void setIdleTimeout(Duration idleTimeout) {
|
||||
this.idleTimeout = idleTimeout;
|
||||
}
|
||||
|
||||
}
|
|
@ -24,9 +24,10 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.autoconfigure.web.server.reactive.ReactiveWebServerConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.server.tomcat.TomcatServerProperties;
|
||||
import org.springframework.boot.autoconfigure.web.server.tomcat.TomcatWebServerConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.server.reactive.ReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.server.reactive.tomcat.TomcatReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.server.tomcat.TomcatConnectorCustomizer;
|
||||
|
@ -46,13 +47,14 @@ import org.springframework.http.ReactiveHttpInputMessage;
|
|||
@AutoConfiguration
|
||||
@ConditionalOnClass({ ReactiveHttpInputMessage.class, Tomcat.class })
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
|
||||
@EnableConfigurationProperties(TomcatServerProperties.class)
|
||||
@Import({ TomcatWebServerConfiguration.class, ReactiveWebServerConfiguration.class })
|
||||
public class TomcatReactiveWebServerAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
TomcatReactiveWebServerFactoryCustomizer tomcatReactiveWebServerFactoryCustomizer(
|
||||
ServerProperties serverProperties) {
|
||||
return new TomcatReactiveWebServerFactoryCustomizer(serverProperties);
|
||||
TomcatServerProperties tomcatProperties) {
|
||||
return new TomcatReactiveWebServerFactoryCustomizer(tomcatProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
|
|
@ -16,18 +16,13 @@
|
|||
|
||||
package org.springframework.boot.autoconfigure.web.server.reactive.tomcat;
|
||||
|
||||
import org.apache.catalina.core.AprLifecycleListener;
|
||||
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties.Tomcat;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties.Tomcat.UseApr;
|
||||
import org.springframework.boot.autoconfigure.web.server.tomcat.TomcatServerProperties;
|
||||
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||
import org.springframework.boot.web.server.reactive.tomcat.TomcatReactiveWebServerFactory;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link WebServerFactoryCustomizer} to apply {@link ServerProperties} to Tomcat reactive
|
||||
* web servers.
|
||||
* {@link WebServerFactoryCustomizer} to apply {@link TomcatServerProperties} to Tomcat
|
||||
* reactive web servers.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
* @since 4.0.0
|
||||
|
@ -35,35 +30,15 @@ import org.springframework.util.Assert;
|
|||
public class TomcatReactiveWebServerFactoryCustomizer
|
||||
implements WebServerFactoryCustomizer<TomcatReactiveWebServerFactory> {
|
||||
|
||||
private final ServerProperties serverProperties;
|
||||
private final TomcatServerProperties tomcatProperties;
|
||||
|
||||
public TomcatReactiveWebServerFactoryCustomizer(ServerProperties serverProperties) {
|
||||
this.serverProperties = serverProperties;
|
||||
public TomcatReactiveWebServerFactoryCustomizer(TomcatServerProperties tomcatProperties) {
|
||||
this.tomcatProperties = tomcatProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(TomcatReactiveWebServerFactory factory) {
|
||||
Tomcat tomcatProperties = this.serverProperties.getTomcat();
|
||||
factory.setDisableMBeanRegistry(!tomcatProperties.getMbeanregistry().isEnabled());
|
||||
factory.setUseApr(getUseApr(tomcatProperties.getUseApr()));
|
||||
}
|
||||
|
||||
private boolean getUseApr(UseApr useApr) {
|
||||
return switch (useApr) {
|
||||
case ALWAYS -> {
|
||||
Assert.state(isAprAvailable(), "APR has been configured to 'ALWAYS', but it's not available");
|
||||
yield true;
|
||||
}
|
||||
case WHEN_AVAILABLE -> isAprAvailable();
|
||||
case NEVER -> false;
|
||||
};
|
||||
}
|
||||
|
||||
private boolean isAprAvailable() {
|
||||
// At least one instance of AprLifecycleListener has to be created for
|
||||
// isAprAvailable() to work
|
||||
new AprLifecycleListener();
|
||||
return AprLifecycleListener.isAprAvailable();
|
||||
factory.setDisableMBeanRegistry(!this.tomcatProperties.getMbeanregistry().isEnabled());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,7 +25,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.web.server.reactive.ReactiveWebServerConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.server.undertow.UndertowServerProperties;
|
||||
import org.springframework.boot.autoconfigure.web.server.undertow.UndertowWebServerConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.server.reactive.ReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.server.reactive.undertow.UndertowReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.server.undertow.UndertowBuilderCustomizer;
|
||||
|
@ -43,6 +45,7 @@ import org.springframework.http.ReactiveHttpInputMessage;
|
|||
@AutoConfiguration
|
||||
@ConditionalOnClass({ ReactiveHttpInputMessage.class, Undertow.class })
|
||||
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
|
||||
@EnableConfigurationProperties(UndertowServerProperties.class)
|
||||
@Import({ UndertowWebServerConfiguration.class, ReactiveWebServerConfiguration.class })
|
||||
public class UndertowReactiveWebServerAutoConfiguration {
|
||||
|
||||
|
|
|
@ -36,8 +36,10 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnNotWarDeplo
|
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
||||
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
|
||||
import org.springframework.boot.autoconfigure.web.server.jetty.JettyServerProperties;
|
||||
import org.springframework.boot.autoconfigure.web.server.jetty.JettyWebServerConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.server.servlet.ServletWebServerConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||
import org.springframework.boot.web.server.jetty.JettyServerCustomizer;
|
||||
import org.springframework.boot.web.server.servlet.ServletWebServerFactory;
|
||||
|
@ -58,6 +60,7 @@ import org.springframework.core.annotation.Order;
|
|||
@AutoConfiguration
|
||||
@ConditionalOnClass({ ServletRequest.class, Server.class, Loader.class, WebAppContext.class })
|
||||
@ConditionalOnWebApplication(type = Type.SERVLET)
|
||||
@EnableConfigurationProperties(JettyServerProperties.class)
|
||||
@Import({ JettyWebServerConfiguration.class, ServletWebServerConfiguration.class })
|
||||
public class JettyServletWebServerAutoConfiguration {
|
||||
|
||||
|
|
|
@ -32,7 +32,9 @@ import org.springframework.boot.autoconfigure.condition.SearchStrategy;
|
|||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.autoconfigure.web.server.servlet.ForwardedHeaderFilterCustomizer;
|
||||
import org.springframework.boot.autoconfigure.web.server.servlet.ServletWebServerConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.server.tomcat.TomcatServerProperties;
|
||||
import org.springframework.boot.autoconfigure.web.server.tomcat.TomcatWebServerConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.server.servlet.ServletWebServerFactory;
|
||||
import org.springframework.boot.web.server.servlet.tomcat.TomcatServletWebServerFactory;
|
||||
import org.springframework.boot.web.server.tomcat.TomcatConnectorCustomizer;
|
||||
|
@ -51,9 +53,16 @@ import org.springframework.context.annotation.Import;
|
|||
@AutoConfiguration
|
||||
@ConditionalOnClass({ ServletRequest.class, Tomcat.class, UpgradeProtocol.class })
|
||||
@ConditionalOnWebApplication(type = Type.SERVLET)
|
||||
@EnableConfigurationProperties(TomcatServerProperties.class)
|
||||
@Import({ ServletWebServerConfiguration.class, TomcatWebServerConfiguration.class })
|
||||
public class TomcatServletWebServerAutoConfiguration {
|
||||
|
||||
private final TomcatServerProperties tomcatProperties;
|
||||
|
||||
public TomcatServletWebServerAutoConfiguration(TomcatServerProperties tomcatProperties) {
|
||||
this.tomcatProperties = tomcatProperties;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
|
||||
TomcatServletWebServerFactory tomcatServletWebServerFactory(
|
||||
|
@ -68,14 +77,15 @@ public class TomcatServletWebServerAutoConfiguration {
|
|||
}
|
||||
|
||||
@Bean
|
||||
TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) {
|
||||
return new TomcatServletWebServerFactoryCustomizer(serverProperties);
|
||||
TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
|
||||
TomcatServerProperties tomcatProperties) {
|
||||
return new TomcatServletWebServerFactoryCustomizer(tomcatProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "server.forward-headers-strategy", havingValue = "framework")
|
||||
ForwardedHeaderFilterCustomizer tomcatForwardedHeaderFilterCustomizer(ServerProperties serverProperties) {
|
||||
return (filter) -> filter.setRelativeRedirects(serverProperties.getTomcat().isUseRelativeRedirects());
|
||||
return (filter) -> filter.setRelativeRedirects(this.tomcatProperties.isUseRelativeRedirects());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,8 +16,7 @@
|
|||
|
||||
package org.springframework.boot.autoconfigure.web.server.servlet.tomcat;
|
||||
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties.Tomcat.UseApr;
|
||||
import org.springframework.boot.autoconfigure.web.server.tomcat.TomcatServerProperties;
|
||||
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||
import org.springframework.boot.web.server.servlet.tomcat.TomcatServletWebServerFactory;
|
||||
import org.springframework.boot.web.server.tomcat.ConfigurableTomcatWebServerFactory;
|
||||
|
@ -25,8 +24,8 @@ import org.springframework.core.Ordered;
|
|||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* {@link WebServerFactoryCustomizer} to apply {@link ServerProperties} to Tomcat web
|
||||
* servers.
|
||||
* {@link WebServerFactoryCustomizer} to apply {@link TomcatServerProperties} to Tomcat
|
||||
* web servers.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Phillip Webb
|
||||
|
@ -34,10 +33,10 @@ import org.springframework.util.ObjectUtils;
|
|||
class TomcatServletWebServerFactoryCustomizer
|
||||
implements WebServerFactoryCustomizer<TomcatServletWebServerFactory>, Ordered {
|
||||
|
||||
private final ServerProperties serverProperties;
|
||||
private final TomcatServerProperties tomcatProperties;
|
||||
|
||||
TomcatServletWebServerFactoryCustomizer(ServerProperties serverProperties) {
|
||||
this.serverProperties = serverProperties;
|
||||
TomcatServletWebServerFactoryCustomizer(TomcatServerProperties tomcatProperties) {
|
||||
this.tomcatProperties = tomcatProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -47,15 +46,14 @@ class TomcatServletWebServerFactoryCustomizer
|
|||
|
||||
@Override
|
||||
public void customize(TomcatServletWebServerFactory factory) {
|
||||
ServerProperties.Tomcat tomcatProperties = this.serverProperties.getTomcat();
|
||||
if (!ObjectUtils.isEmpty(tomcatProperties.getAdditionalTldSkipPatterns())) {
|
||||
factory.getTldSkipPatterns().addAll(tomcatProperties.getAdditionalTldSkipPatterns());
|
||||
if (!ObjectUtils.isEmpty(this.tomcatProperties.getAdditionalTldSkipPatterns())) {
|
||||
factory.getTldSkipPatterns().addAll(this.tomcatProperties.getAdditionalTldSkipPatterns());
|
||||
}
|
||||
if (tomcatProperties.getRedirectContextRoot() != null) {
|
||||
customizeRedirectContextRoot(factory, tomcatProperties.getRedirectContextRoot());
|
||||
if (this.tomcatProperties.getRedirectContextRoot() != null) {
|
||||
customizeRedirectContextRoot(factory, this.tomcatProperties.getRedirectContextRoot());
|
||||
}
|
||||
customizeUseRelativeRedirects(factory, tomcatProperties.isUseRelativeRedirects());
|
||||
factory.setDisableMBeanRegistry(!tomcatProperties.getMbeanregistry().isEnabled());
|
||||
customizeUseRelativeRedirects(factory, this.tomcatProperties.isUseRelativeRedirects());
|
||||
factory.setDisableMBeanRegistry(!this.tomcatProperties.getMbeanregistry().isEnabled());
|
||||
}
|
||||
|
||||
private void customizeRedirectContextRoot(ConfigurableTomcatWebServerFactory factory, boolean redirectContextRoot) {
|
||||
|
|
|
@ -29,9 +29,10 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
|
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
|
||||
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.autoconfigure.web.server.servlet.ServletWebServerConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.server.undertow.UndertowServerProperties;
|
||||
import org.springframework.boot.autoconfigure.web.server.undertow.UndertowWebServerConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.server.servlet.ServletWebServerFactory;
|
||||
import org.springframework.boot.web.server.servlet.undertow.UndertowDeploymentInfoCustomizer;
|
||||
import org.springframework.boot.web.server.servlet.undertow.UndertowServletWebServerFactory;
|
||||
|
@ -50,6 +51,7 @@ import org.springframework.context.annotation.Import;
|
|||
@AutoConfiguration
|
||||
@ConditionalOnClass({ ServletRequest.class, Undertow.class, SslClientAuthMode.class })
|
||||
@ConditionalOnWebApplication(type = Type.SERVLET)
|
||||
@EnableConfigurationProperties(UndertowServerProperties.class)
|
||||
@Import({ UndertowWebServerConfiguration.class, ServletWebServerConfiguration.class })
|
||||
public class UndertowServletWebServerAutoConfiguration {
|
||||
|
||||
|
@ -66,8 +68,8 @@ public class UndertowServletWebServerAutoConfiguration {
|
|||
|
||||
@Bean
|
||||
UndertowServletWebServerFactoryCustomizer undertowServletWebServerFactoryCustomizer(
|
||||
ServerProperties serverProperties) {
|
||||
return new UndertowServletWebServerFactoryCustomizer(serverProperties);
|
||||
UndertowServerProperties undertowProperties) {
|
||||
return new UndertowServletWebServerFactoryCustomizer(undertowProperties);
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.boot.autoconfigure.web.server.servlet.undertow;
|
||||
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.autoconfigure.web.server.undertow.UndertowServerProperties;
|
||||
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||
import org.springframework.boot.web.server.servlet.undertow.UndertowServletWebServerFactory;
|
||||
|
||||
|
@ -28,16 +29,16 @@ import org.springframework.boot.web.server.servlet.undertow.UndertowServletWebSe
|
|||
*/
|
||||
class UndertowServletWebServerFactoryCustomizer implements WebServerFactoryCustomizer<UndertowServletWebServerFactory> {
|
||||
|
||||
private final ServerProperties serverProperties;
|
||||
private final UndertowServerProperties undertowProperties;
|
||||
|
||||
UndertowServletWebServerFactoryCustomizer(ServerProperties serverProperties) {
|
||||
this.serverProperties = serverProperties;
|
||||
UndertowServletWebServerFactoryCustomizer(UndertowServerProperties undertowProperties) {
|
||||
this.undertowProperties = undertowProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(UndertowServletWebServerFactory factory) {
|
||||
factory.setEagerFilterInit(this.serverProperties.getUndertow().isEagerFilterInit());
|
||||
factory.setPreservePathOnForward(this.serverProperties.getUndertow().isPreservePathOnForward());
|
||||
factory.setEagerFilterInit(this.undertowProperties.isEagerFilterInit());
|
||||
factory.setPreservePathOnForward(this.undertowProperties.isPreservePathOnForward());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,840 @@
|
|||
/*
|
||||
* 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.server.tomcat;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.convert.DurationUnit;
|
||||
import org.springframework.util.unit.DataSize;
|
||||
|
||||
/**
|
||||
* Tomcat server properties.
|
||||
*
|
||||
* @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 3.5.0
|
||||
*/
|
||||
@ConfigurationProperties("server.tomcat")
|
||||
public class TomcatServerProperties {
|
||||
|
||||
/**
|
||||
* Tomcat base directory. If not specified, a temporary directory is used.
|
||||
*/
|
||||
private File basedir;
|
||||
|
||||
/**
|
||||
* Delay between the invocation of backgroundProcess methods. If a duration suffix is
|
||||
* not specified, seconds will be used.
|
||||
*/
|
||||
@DurationUnit(ChronoUnit.SECONDS)
|
||||
private Duration backgroundProcessorDelay = Duration.ofSeconds(10);
|
||||
|
||||
/**
|
||||
* Maximum size of the form content in any HTTP post request.
|
||||
*/
|
||||
private DataSize maxHttpFormPostSize = DataSize.ofMegabytes(2);
|
||||
|
||||
/**
|
||||
* Maximum amount of request body to swallow.
|
||||
*/
|
||||
private DataSize maxSwallowSize = DataSize.ofMegabytes(2);
|
||||
|
||||
/**
|
||||
* Whether requests to the context root should be redirected by appending a / to the
|
||||
* path. When using SSL terminated at a proxy, this property should be set to false.
|
||||
*/
|
||||
private Boolean redirectContextRoot = true;
|
||||
|
||||
/**
|
||||
* Whether HTTP 1.1 and later location headers generated by a call to sendRedirect
|
||||
* will use relative or absolute redirects.
|
||||
*/
|
||||
private boolean useRelativeRedirects;
|
||||
|
||||
/**
|
||||
* Character encoding to use to decode the URI.
|
||||
*/
|
||||
private Charset uriEncoding = StandardCharsets.UTF_8;
|
||||
|
||||
/**
|
||||
* Maximum number of connections that the server accepts and processes at any given
|
||||
* time. Once the limit has been reached, the operating system may still accept
|
||||
* connections based on the "acceptCount" property.
|
||||
*/
|
||||
private int maxConnections = 8192;
|
||||
|
||||
/**
|
||||
* Maximum queue length for incoming connection requests when all possible request
|
||||
* processing threads are in use.
|
||||
*/
|
||||
private int acceptCount = 100;
|
||||
|
||||
/**
|
||||
* Maximum number of idle processors that will be retained in the cache and reused
|
||||
* with a subsequent request. When set to -1 the cache will be unlimited with a
|
||||
* theoretical maximum size equal to the maximum number of connections.
|
||||
*/
|
||||
private int processorCache = 200;
|
||||
|
||||
/**
|
||||
* Time to wait for another HTTP request before the connection is closed. When not set
|
||||
* the connectionTimeout is used. When set to -1 there will be no timeout.
|
||||
*/
|
||||
private Duration keepAliveTimeout;
|
||||
|
||||
/**
|
||||
* Maximum number of HTTP requests that can be pipelined before the connection is
|
||||
* closed. When set to 0 or 1, keep-alive and pipelining are disabled. When set to -1,
|
||||
* an unlimited number of pipelined or keep-alive requests are allowed.
|
||||
*/
|
||||
private int maxKeepAliveRequests = 100;
|
||||
|
||||
/**
|
||||
* List of additional patterns that match jars to ignore for TLD scanning. The special
|
||||
* '?' and '*' characters can be used in the pattern to match one and only one
|
||||
* character and zero or more characters respectively.
|
||||
*/
|
||||
private List<String> additionalTldSkipPatterns = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* List of additional unencoded characters that should be allowed in URI paths. Only
|
||||
* "< > [ \ ] ^ ` { | }" are allowed.
|
||||
*/
|
||||
private List<Character> relaxedPathChars = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* List of additional unencoded characters that should be allowed in URI query
|
||||
* strings. Only "< > [ \ ] ^ ` { | }" are allowed.
|
||||
*/
|
||||
private List<Character> relaxedQueryChars = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Amount of time the connector will wait, after accepting a connection, for the
|
||||
* request URI line to be presented.
|
||||
*/
|
||||
private Duration connectionTimeout;
|
||||
|
||||
/**
|
||||
* Maximum size of the HTTP response header.
|
||||
*/
|
||||
private DataSize maxHttpResponseHeaderSize = DataSize.ofKilobytes(8);
|
||||
|
||||
/**
|
||||
* Maximum number of parameters (GET plus POST) that will be automatically parsed by
|
||||
* the container. A value of less than 0 means no limit.
|
||||
*/
|
||||
private int maxParameterCount = 10000;
|
||||
|
||||
/**
|
||||
* Whether to use APR.
|
||||
*/
|
||||
private UseApr useApr = UseApr.NEVER;
|
||||
|
||||
/**
|
||||
* Access log configuration.
|
||||
*/
|
||||
private final Accesslog accesslog = new Accesslog();
|
||||
|
||||
/**
|
||||
* Thread related configuration.
|
||||
*/
|
||||
private final Threads threads = new Threads();
|
||||
|
||||
/**
|
||||
* Static resource configuration.
|
||||
*/
|
||||
private final Resource resource = new Resource();
|
||||
|
||||
/**
|
||||
* Modeler MBean Registry configuration.
|
||||
*/
|
||||
private final Mbeanregistry mbeanregistry = new Mbeanregistry();
|
||||
|
||||
/**
|
||||
* Remote Ip Valve configuration.
|
||||
*/
|
||||
private final Remoteip remoteip = new Remoteip();
|
||||
|
||||
public Duration getBackgroundProcessorDelay() {
|
||||
return this.backgroundProcessorDelay;
|
||||
}
|
||||
|
||||
public void setBackgroundProcessorDelay(Duration backgroundProcessorDelay) {
|
||||
this.backgroundProcessorDelay = backgroundProcessorDelay;
|
||||
}
|
||||
|
||||
public File getBasedir() {
|
||||
return this.basedir;
|
||||
}
|
||||
|
||||
public void setBasedir(File basedir) {
|
||||
this.basedir = basedir;
|
||||
}
|
||||
|
||||
public Boolean getRedirectContextRoot() {
|
||||
return this.redirectContextRoot;
|
||||
}
|
||||
|
||||
public void setRedirectContextRoot(Boolean redirectContextRoot) {
|
||||
this.redirectContextRoot = redirectContextRoot;
|
||||
}
|
||||
|
||||
public boolean isUseRelativeRedirects() {
|
||||
return this.useRelativeRedirects;
|
||||
}
|
||||
|
||||
public void setUseRelativeRedirects(boolean useRelativeRedirects) {
|
||||
this.useRelativeRedirects = useRelativeRedirects;
|
||||
}
|
||||
|
||||
public Charset getUriEncoding() {
|
||||
return this.uriEncoding;
|
||||
}
|
||||
|
||||
public void setUriEncoding(Charset uriEncoding) {
|
||||
this.uriEncoding = uriEncoding;
|
||||
}
|
||||
|
||||
public int getMaxConnections() {
|
||||
return this.maxConnections;
|
||||
}
|
||||
|
||||
public void setMaxConnections(int maxConnections) {
|
||||
this.maxConnections = maxConnections;
|
||||
}
|
||||
|
||||
public DataSize getMaxSwallowSize() {
|
||||
return this.maxSwallowSize;
|
||||
}
|
||||
|
||||
public void setMaxSwallowSize(DataSize maxSwallowSize) {
|
||||
this.maxSwallowSize = maxSwallowSize;
|
||||
}
|
||||
|
||||
public int getAcceptCount() {
|
||||
return this.acceptCount;
|
||||
}
|
||||
|
||||
public void setAcceptCount(int acceptCount) {
|
||||
this.acceptCount = acceptCount;
|
||||
}
|
||||
|
||||
public int getProcessorCache() {
|
||||
return this.processorCache;
|
||||
}
|
||||
|
||||
public void setProcessorCache(int processorCache) {
|
||||
this.processorCache = processorCache;
|
||||
}
|
||||
|
||||
public Duration getKeepAliveTimeout() {
|
||||
return this.keepAliveTimeout;
|
||||
}
|
||||
|
||||
public void setKeepAliveTimeout(Duration keepAliveTimeout) {
|
||||
this.keepAliveTimeout = keepAliveTimeout;
|
||||
}
|
||||
|
||||
public int getMaxKeepAliveRequests() {
|
||||
return this.maxKeepAliveRequests;
|
||||
}
|
||||
|
||||
public void setMaxKeepAliveRequests(int maxKeepAliveRequests) {
|
||||
this.maxKeepAliveRequests = maxKeepAliveRequests;
|
||||
}
|
||||
|
||||
public List<String> getAdditionalTldSkipPatterns() {
|
||||
return this.additionalTldSkipPatterns;
|
||||
}
|
||||
|
||||
public void setAdditionalTldSkipPatterns(List<String> additionalTldSkipPatterns) {
|
||||
this.additionalTldSkipPatterns = additionalTldSkipPatterns;
|
||||
}
|
||||
|
||||
public List<Character> getRelaxedPathChars() {
|
||||
return this.relaxedPathChars;
|
||||
}
|
||||
|
||||
public void setRelaxedPathChars(List<Character> relaxedPathChars) {
|
||||
this.relaxedPathChars = relaxedPathChars;
|
||||
}
|
||||
|
||||
public List<Character> getRelaxedQueryChars() {
|
||||
return this.relaxedQueryChars;
|
||||
}
|
||||
|
||||
public void setRelaxedQueryChars(List<Character> relaxedQueryChars) {
|
||||
this.relaxedQueryChars = relaxedQueryChars;
|
||||
}
|
||||
|
||||
public Duration getConnectionTimeout() {
|
||||
return this.connectionTimeout;
|
||||
}
|
||||
|
||||
public void setConnectionTimeout(Duration connectionTimeout) {
|
||||
this.connectionTimeout = connectionTimeout;
|
||||
}
|
||||
|
||||
public DataSize getMaxHttpResponseHeaderSize() {
|
||||
return this.maxHttpResponseHeaderSize;
|
||||
}
|
||||
|
||||
public void setMaxHttpResponseHeaderSize(DataSize maxHttpResponseHeaderSize) {
|
||||
this.maxHttpResponseHeaderSize = maxHttpResponseHeaderSize;
|
||||
}
|
||||
|
||||
public DataSize getMaxHttpFormPostSize() {
|
||||
return this.maxHttpFormPostSize;
|
||||
}
|
||||
|
||||
public void setMaxHttpFormPostSize(DataSize maxHttpFormPostSize) {
|
||||
this.maxHttpFormPostSize = maxHttpFormPostSize;
|
||||
}
|
||||
|
||||
public int getMaxParameterCount() {
|
||||
return this.maxParameterCount;
|
||||
}
|
||||
|
||||
public void setMaxParameterCount(int maxParameterCount) {
|
||||
this.maxParameterCount = maxParameterCount;
|
||||
}
|
||||
|
||||
public UseApr getUseApr() {
|
||||
return this.useApr;
|
||||
}
|
||||
|
||||
public void setUseApr(UseApr useApr) {
|
||||
this.useApr = useApr;
|
||||
}
|
||||
|
||||
public Accesslog getAccesslog() {
|
||||
return this.accesslog;
|
||||
}
|
||||
|
||||
public Threads getThreads() {
|
||||
return this.threads;
|
||||
}
|
||||
|
||||
public Resource getResource() {
|
||||
return this.resource;
|
||||
}
|
||||
|
||||
public Mbeanregistry getMbeanregistry() {
|
||||
return this.mbeanregistry;
|
||||
}
|
||||
|
||||
public Remoteip getRemoteip() {
|
||||
return this.remoteip;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tomcat access log properties.
|
||||
*/
|
||||
public static class Accesslog {
|
||||
|
||||
/**
|
||||
* Enable access log.
|
||||
*/
|
||||
private boolean enabled = false;
|
||||
|
||||
/**
|
||||
* Whether logging of the request will only be enabled if
|
||||
* "ServletRequest.getAttribute(conditionIf)" does not yield null.
|
||||
*/
|
||||
private String conditionIf;
|
||||
|
||||
/**
|
||||
* Whether logging of the request will only be enabled if
|
||||
* "ServletRequest.getAttribute(conditionUnless)" yield null.
|
||||
*/
|
||||
private String conditionUnless;
|
||||
|
||||
/**
|
||||
* Format pattern for access logs.
|
||||
*/
|
||||
private String pattern = "common";
|
||||
|
||||
/**
|
||||
* Directory in which log files are created. Can be absolute or relative to the
|
||||
* Tomcat base dir.
|
||||
*/
|
||||
private String directory = "logs";
|
||||
|
||||
/**
|
||||
* Log file name prefix.
|
||||
*/
|
||||
protected String prefix = "access_log";
|
||||
|
||||
/**
|
||||
* Log file name suffix.
|
||||
*/
|
||||
private String suffix = ".log";
|
||||
|
||||
/**
|
||||
* Character set used by the log file. Default to the system default character
|
||||
* set.
|
||||
*/
|
||||
private String encoding;
|
||||
|
||||
/**
|
||||
* Locale used to format timestamps in log entries and in log file name suffix.
|
||||
* Default to the default locale of the Java process.
|
||||
*/
|
||||
private String locale;
|
||||
|
||||
/**
|
||||
* Whether to check for log file existence so it can be recreated if an external
|
||||
* process has renamed it.
|
||||
*/
|
||||
private boolean checkExists = false;
|
||||
|
||||
/**
|
||||
* Whether to enable access log rotation.
|
||||
*/
|
||||
private boolean rotate = true;
|
||||
|
||||
/**
|
||||
* Whether to defer inclusion of the date stamp in the file name until rotate
|
||||
* time.
|
||||
*/
|
||||
private boolean renameOnRotate = false;
|
||||
|
||||
/**
|
||||
* Number of days to retain the access log files before they are removed.
|
||||
*/
|
||||
private int maxDays = -1;
|
||||
|
||||
/**
|
||||
* Date format to place in the log file name.
|
||||
*/
|
||||
private String fileDateFormat = ".yyyy-MM-dd";
|
||||
|
||||
/**
|
||||
* Whether to use IPv6 canonical representation format as defined by RFC 5952.
|
||||
*/
|
||||
private boolean ipv6Canonical = false;
|
||||
|
||||
/**
|
||||
* Set request attributes for the IP address, Hostname, protocol, and port used
|
||||
* for the request.
|
||||
*/
|
||||
private boolean requestAttributesEnabled = false;
|
||||
|
||||
/**
|
||||
* Whether to buffer output such that it is flushed only periodically.
|
||||
*/
|
||||
private boolean buffered = true;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getConditionIf() {
|
||||
return this.conditionIf;
|
||||
}
|
||||
|
||||
public void setConditionIf(String conditionIf) {
|
||||
this.conditionIf = conditionIf;
|
||||
}
|
||||
|
||||
public String getConditionUnless() {
|
||||
return this.conditionUnless;
|
||||
}
|
||||
|
||||
public void setConditionUnless(String conditionUnless) {
|
||||
this.conditionUnless = conditionUnless;
|
||||
}
|
||||
|
||||
public String getPattern() {
|
||||
return this.pattern;
|
||||
}
|
||||
|
||||
public void setPattern(String pattern) {
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
public String getDirectory() {
|
||||
return this.directory;
|
||||
}
|
||||
|
||||
public void setDirectory(String directory) {
|
||||
this.directory = directory;
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
return this.prefix;
|
||||
}
|
||||
|
||||
public void setPrefix(String prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
public String getSuffix() {
|
||||
return this.suffix;
|
||||
}
|
||||
|
||||
public void setSuffix(String suffix) {
|
||||
this.suffix = suffix;
|
||||
}
|
||||
|
||||
public String getEncoding() {
|
||||
return this.encoding;
|
||||
}
|
||||
|
||||
public void setEncoding(String encoding) {
|
||||
this.encoding = encoding;
|
||||
}
|
||||
|
||||
public String getLocale() {
|
||||
return this.locale;
|
||||
}
|
||||
|
||||
public void setLocale(String locale) {
|
||||
this.locale = locale;
|
||||
}
|
||||
|
||||
public boolean isCheckExists() {
|
||||
return this.checkExists;
|
||||
}
|
||||
|
||||
public void setCheckExists(boolean checkExists) {
|
||||
this.checkExists = checkExists;
|
||||
}
|
||||
|
||||
public boolean isRotate() {
|
||||
return this.rotate;
|
||||
}
|
||||
|
||||
public void setRotate(boolean rotate) {
|
||||
this.rotate = rotate;
|
||||
}
|
||||
|
||||
public boolean isRenameOnRotate() {
|
||||
return this.renameOnRotate;
|
||||
}
|
||||
|
||||
public void setRenameOnRotate(boolean renameOnRotate) {
|
||||
this.renameOnRotate = renameOnRotate;
|
||||
}
|
||||
|
||||
public int getMaxDays() {
|
||||
return this.maxDays;
|
||||
}
|
||||
|
||||
public void setMaxDays(int maxDays) {
|
||||
this.maxDays = maxDays;
|
||||
}
|
||||
|
||||
public String getFileDateFormat() {
|
||||
return this.fileDateFormat;
|
||||
}
|
||||
|
||||
public void setFileDateFormat(String fileDateFormat) {
|
||||
this.fileDateFormat = fileDateFormat;
|
||||
}
|
||||
|
||||
public boolean isIpv6Canonical() {
|
||||
return this.ipv6Canonical;
|
||||
}
|
||||
|
||||
public void setIpv6Canonical(boolean ipv6Canonical) {
|
||||
this.ipv6Canonical = ipv6Canonical;
|
||||
}
|
||||
|
||||
public boolean isRequestAttributesEnabled() {
|
||||
return this.requestAttributesEnabled;
|
||||
}
|
||||
|
||||
public void setRequestAttributesEnabled(boolean requestAttributesEnabled) {
|
||||
this.requestAttributesEnabled = requestAttributesEnabled;
|
||||
}
|
||||
|
||||
public boolean isBuffered() {
|
||||
return this.buffered;
|
||||
}
|
||||
|
||||
public void setBuffered(boolean buffered) {
|
||||
this.buffered = buffered;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tomcat thread properties.
|
||||
*/
|
||||
public static class Threads {
|
||||
|
||||
/**
|
||||
* Maximum amount of worker threads. Doesn't have an effect if virtual threads are
|
||||
* enabled.
|
||||
*/
|
||||
private int max = 200;
|
||||
|
||||
/**
|
||||
* Minimum amount of worker threads. Doesn't have an effect if virtual threads are
|
||||
* enabled.
|
||||
*/
|
||||
private int minSpare = 10;
|
||||
|
||||
/**
|
||||
* Maximum capacity of the thread pool's backing queue. This setting only has an
|
||||
* effect if the value is greater than 0.
|
||||
*/
|
||||
private int maxQueueCapacity = 2147483647;
|
||||
|
||||
public int getMax() {
|
||||
return this.max;
|
||||
}
|
||||
|
||||
public void setMax(int max) {
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
public int getMinSpare() {
|
||||
return this.minSpare;
|
||||
}
|
||||
|
||||
public void setMinSpare(int minSpare) {
|
||||
this.minSpare = minSpare;
|
||||
}
|
||||
|
||||
public int getMaxQueueCapacity() {
|
||||
return this.maxQueueCapacity;
|
||||
}
|
||||
|
||||
public void setMaxQueueCapacity(int maxQueueCapacity) {
|
||||
this.maxQueueCapacity = maxQueueCapacity;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tomcat static resource properties.
|
||||
*/
|
||||
public static class Resource {
|
||||
|
||||
/**
|
||||
* Whether static resource caching is permitted for this web application.
|
||||
*/
|
||||
private boolean allowCaching = true;
|
||||
|
||||
/**
|
||||
* Time-to-live of the static resource cache.
|
||||
*/
|
||||
private Duration cacheTtl;
|
||||
|
||||
public boolean isAllowCaching() {
|
||||
return this.allowCaching;
|
||||
}
|
||||
|
||||
public void setAllowCaching(boolean allowCaching) {
|
||||
this.allowCaching = allowCaching;
|
||||
}
|
||||
|
||||
public Duration getCacheTtl() {
|
||||
return this.cacheTtl;
|
||||
}
|
||||
|
||||
public void setCacheTtl(Duration cacheTtl) {
|
||||
this.cacheTtl = cacheTtl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Mbeanregistry {
|
||||
|
||||
/**
|
||||
* Whether Tomcat's MBean Registry should be enabled.
|
||||
*/
|
||||
private boolean enabled;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Remoteip {
|
||||
|
||||
/**
|
||||
* Regular expression that matches proxies that are to be trusted.
|
||||
*/
|
||||
private String internalProxies = "10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" // 10/8
|
||||
+ "192\\.168\\.\\d{1,3}\\.\\d{1,3}|" // 192.168/16
|
||||
+ "169\\.254\\.\\d{1,3}\\.\\d{1,3}|" // 169.254/16
|
||||
+ "127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" // 127/8
|
||||
+ "100\\.6[4-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" // 100.64.0.0/10
|
||||
+ "100\\.[7-9]{1}\\d{1}\\.\\d{1,3}\\.\\d{1,3}|" // 100.64.0.0/10
|
||||
+ "100\\.1[0-1]{1}\\d{1}\\.\\d{1,3}\\.\\d{1,3}|" // 100.64.0.0/10
|
||||
+ "100\\.12[0-7]{1}\\.\\d{1,3}\\.\\d{1,3}|" // 100.64.0.0/10
|
||||
+ "172\\.1[6-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" // 172.16/12
|
||||
+ "172\\.2[0-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" // 172.16/12
|
||||
+ "172\\.3[0-1]{1}\\.\\d{1,3}\\.\\d{1,3}|" // 172.16/12
|
||||
+ "0:0:0:0:0:0:0:1|" // 0:0:0:0:0:0:0:1
|
||||
+ "::1|" // ::1
|
||||
+ "fe[89ab]\\p{XDigit}:.*|" //
|
||||
+ "f[cd]\\p{XDigit}{2}+:.*";
|
||||
|
||||
/**
|
||||
* Header that holds the incoming protocol, usually named "X-Forwarded-Proto".
|
||||
*/
|
||||
private String protocolHeader;
|
||||
|
||||
/**
|
||||
* Value of the protocol header indicating whether the incoming request uses SSL.
|
||||
*/
|
||||
private String protocolHeaderHttpsValue = "https";
|
||||
|
||||
/**
|
||||
* Name of the HTTP header from which the remote host is extracted.
|
||||
*/
|
||||
private String hostHeader = "X-Forwarded-Host";
|
||||
|
||||
/**
|
||||
* Name of the HTTP header used to override the original port value.
|
||||
*/
|
||||
private String portHeader = "X-Forwarded-Port";
|
||||
|
||||
/**
|
||||
* Name of the HTTP header from which the remote IP is extracted. For instance,
|
||||
* 'X-FORWARDED-FOR'.
|
||||
*/
|
||||
private String remoteIpHeader;
|
||||
|
||||
/**
|
||||
* Regular expression defining proxies that are trusted when they appear in the
|
||||
* "remote-ip-header" header.
|
||||
*/
|
||||
private String trustedProxies;
|
||||
|
||||
public String getInternalProxies() {
|
||||
return this.internalProxies;
|
||||
}
|
||||
|
||||
public void setInternalProxies(String internalProxies) {
|
||||
this.internalProxies = internalProxies;
|
||||
}
|
||||
|
||||
public String getProtocolHeader() {
|
||||
return this.protocolHeader;
|
||||
}
|
||||
|
||||
public void setProtocolHeader(String protocolHeader) {
|
||||
this.protocolHeader = protocolHeader;
|
||||
}
|
||||
|
||||
public String getProtocolHeaderHttpsValue() {
|
||||
return this.protocolHeaderHttpsValue;
|
||||
}
|
||||
|
||||
public String getHostHeader() {
|
||||
return this.hostHeader;
|
||||
}
|
||||
|
||||
public void setHostHeader(String hostHeader) {
|
||||
this.hostHeader = hostHeader;
|
||||
}
|
||||
|
||||
public void setProtocolHeaderHttpsValue(String protocolHeaderHttpsValue) {
|
||||
this.protocolHeaderHttpsValue = protocolHeaderHttpsValue;
|
||||
}
|
||||
|
||||
public String getPortHeader() {
|
||||
return this.portHeader;
|
||||
}
|
||||
|
||||
public void setPortHeader(String portHeader) {
|
||||
this.portHeader = portHeader;
|
||||
}
|
||||
|
||||
public String getRemoteIpHeader() {
|
||||
return this.remoteIpHeader;
|
||||
}
|
||||
|
||||
public void setRemoteIpHeader(String remoteIpHeader) {
|
||||
this.remoteIpHeader = remoteIpHeader;
|
||||
}
|
||||
|
||||
public String getTrustedProxies() {
|
||||
return this.trustedProxies;
|
||||
}
|
||||
|
||||
public void setTrustedProxies(String trustedProxies) {
|
||||
this.trustedProxies = trustedProxies;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* When to use APR.
|
||||
*/
|
||||
public enum UseApr {
|
||||
|
||||
/**
|
||||
* Always use APR and fail if it's not available.
|
||||
*/
|
||||
ALWAYS,
|
||||
|
||||
/**
|
||||
* Use APR if it is available.
|
||||
*/
|
||||
WHEN_AVAILABLE,
|
||||
|
||||
/**
|
||||
* Never use APR.
|
||||
*/
|
||||
NEVER
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -43,8 +43,8 @@ public class TomcatWebServerConfiguration {
|
|||
|
||||
@Bean
|
||||
TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(Environment environment,
|
||||
ServerProperties serverProperties) {
|
||||
return new TomcatWebServerFactoryCustomizer(environment, serverProperties);
|
||||
ServerProperties serverProperties, TomcatServerProperties tomcatProperties) {
|
||||
return new TomcatWebServerFactoryCustomizer(environment, serverProperties, tomcatProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
|
|
@ -35,9 +35,9 @@ import org.apache.coyote.http2.Http2Protocol;
|
|||
import org.springframework.boot.autoconfigure.web.ErrorProperties;
|
||||
import org.springframework.boot.autoconfigure.web.ErrorProperties.IncludeAttribute;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties.Tomcat.Accesslog;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties.Tomcat.Remoteip;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties.Tomcat.UseApr;
|
||||
import org.springframework.boot.autoconfigure.web.server.tomcat.TomcatServerProperties.Accesslog;
|
||||
import org.springframework.boot.autoconfigure.web.server.tomcat.TomcatServerProperties.Remoteip;
|
||||
import org.springframework.boot.autoconfigure.web.server.tomcat.TomcatServerProperties.UseApr;
|
||||
import org.springframework.boot.cloud.CloudPlatform;
|
||||
import org.springframework.boot.context.properties.PropertyMapper;
|
||||
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||
|
@ -75,9 +75,13 @@ public class TomcatWebServerFactoryCustomizer
|
|||
|
||||
private final ServerProperties serverProperties;
|
||||
|
||||
public TomcatWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {
|
||||
private final TomcatServerProperties tomcatProperties;
|
||||
|
||||
public TomcatWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties,
|
||||
TomcatServerProperties tomcatProperties) {
|
||||
this.environment = environment;
|
||||
this.serverProperties = serverProperties;
|
||||
this.tomcatProperties = tomcatProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -88,15 +92,14 @@ public class TomcatWebServerFactoryCustomizer
|
|||
@Override
|
||||
@SuppressWarnings("removal")
|
||||
public void customize(ConfigurableTomcatWebServerFactory factory) {
|
||||
ServerProperties.Tomcat properties = this.serverProperties.getTomcat();
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
map.from(properties::getBasedir).to(factory::setBaseDirectory);
|
||||
map.from(properties::getBackgroundProcessorDelay)
|
||||
map.from(this.tomcatProperties::getBasedir).to(factory::setBaseDirectory);
|
||||
map.from(this.tomcatProperties::getBackgroundProcessorDelay)
|
||||
.as(Duration::getSeconds)
|
||||
.as(Long::intValue)
|
||||
.to(factory::setBackgroundProcessorDelay);
|
||||
customizeRemoteIpValve(factory);
|
||||
ServerProperties.Tomcat.Threads threadProperties = properties.getThreads();
|
||||
TomcatServerProperties.Threads threadProperties = this.tomcatProperties.getThreads();
|
||||
map.from(threadProperties::getMax)
|
||||
.when(this::isPositive)
|
||||
.to((maxThreads) -> customizeMaxThreads(factory, maxThreads));
|
||||
|
@ -110,48 +113,48 @@ public class TomcatWebServerFactoryCustomizer
|
|||
.asInt(DataSize::toBytes)
|
||||
.when(this::isPositive)
|
||||
.to((maxHttpRequestHeaderSize) -> customizeMaxHttpRequestHeaderSize(factory, maxHttpRequestHeaderSize));
|
||||
map.from(properties::getMaxHttpResponseHeaderSize)
|
||||
map.from(this.tomcatProperties::getMaxHttpResponseHeaderSize)
|
||||
.asInt(DataSize::toBytes)
|
||||
.when(this::isPositive)
|
||||
.to((maxHttpResponseHeaderSize) -> customizeMaxHttpResponseHeaderSize(factory, maxHttpResponseHeaderSize));
|
||||
map.from(properties::getMaxSwallowSize)
|
||||
map.from(this.tomcatProperties::getMaxSwallowSize)
|
||||
.asInt(DataSize::toBytes)
|
||||
.to((maxSwallowSize) -> customizeMaxSwallowSize(factory, maxSwallowSize));
|
||||
map.from(properties::getMaxHttpFormPostSize)
|
||||
map.from(this.tomcatProperties::getMaxHttpFormPostSize)
|
||||
.asInt(DataSize::toBytes)
|
||||
.when((maxHttpFormPostSize) -> maxHttpFormPostSize != 0)
|
||||
.to((maxHttpFormPostSize) -> customizeMaxHttpFormPostSize(factory, maxHttpFormPostSize));
|
||||
map.from(properties::getMaxParameterCount)
|
||||
map.from(this.tomcatProperties::getMaxParameterCount)
|
||||
.to((maxParameterCount) -> customizeMaxParameterCount(factory, maxParameterCount));
|
||||
map.from(properties::getAccesslog)
|
||||
.when(ServerProperties.Tomcat.Accesslog::isEnabled)
|
||||
map.from(this.tomcatProperties::getAccesslog)
|
||||
.when(TomcatServerProperties.Accesslog::isEnabled)
|
||||
.to((enabled) -> customizeAccessLog(factory));
|
||||
map.from(properties::getUriEncoding).to(factory::setUriEncoding);
|
||||
map.from(properties::getConnectionTimeout)
|
||||
map.from(this.tomcatProperties::getUriEncoding).to(factory::setUriEncoding);
|
||||
map.from(this.tomcatProperties::getConnectionTimeout)
|
||||
.to((connectionTimeout) -> customizeConnectionTimeout(factory, connectionTimeout));
|
||||
map.from(properties::getMaxConnections)
|
||||
map.from(this.tomcatProperties::getMaxConnections)
|
||||
.when(this::isPositive)
|
||||
.to((maxConnections) -> customizeMaxConnections(factory, maxConnections));
|
||||
map.from(properties::getAcceptCount)
|
||||
map.from(this.tomcatProperties::getAcceptCount)
|
||||
.when(this::isPositive)
|
||||
.to((acceptCount) -> customizeAcceptCount(factory, acceptCount));
|
||||
map.from(properties::getProcessorCache)
|
||||
map.from(this.tomcatProperties::getProcessorCache)
|
||||
.to((processorCache) -> customizeProcessorCache(factory, processorCache));
|
||||
map.from(properties::getKeepAliveTimeout)
|
||||
map.from(this.tomcatProperties::getKeepAliveTimeout)
|
||||
.to((keepAliveTimeout) -> customizeKeepAliveTimeout(factory, keepAliveTimeout));
|
||||
map.from(properties::getMaxKeepAliveRequests)
|
||||
map.from(this.tomcatProperties::getMaxKeepAliveRequests)
|
||||
.to((maxKeepAliveRequests) -> customizeMaxKeepAliveRequests(factory, maxKeepAliveRequests));
|
||||
map.from(properties::getRelaxedPathChars)
|
||||
map.from(this.tomcatProperties::getRelaxedPathChars)
|
||||
.as(this::joinCharacters)
|
||||
.whenHasText()
|
||||
.to((relaxedChars) -> customizeRelaxedPathChars(factory, relaxedChars));
|
||||
map.from(properties::getRelaxedQueryChars)
|
||||
map.from(this.tomcatProperties::getRelaxedQueryChars)
|
||||
.as(this::joinCharacters)
|
||||
.whenHasText()
|
||||
.to((relaxedChars) -> customizeRelaxedQueryChars(factory, relaxedChars));
|
||||
customizeStaticResources(factory);
|
||||
customizeErrorReportValve(this.serverProperties.getError(), factory);
|
||||
factory.setUseApr(getUseApr(this.serverProperties.getTomcat().getUseApr()));
|
||||
factory.setUseApr(getUseApr(this.tomcatProperties.getUseApr()));
|
||||
}
|
||||
|
||||
private boolean getUseApr(UseApr useApr) {
|
||||
|
@ -245,7 +248,7 @@ public class TomcatWebServerFactoryCustomizer
|
|||
}
|
||||
|
||||
private void customizeRemoteIpValve(ConfigurableTomcatWebServerFactory factory) {
|
||||
Remoteip remoteIpProperties = this.serverProperties.getTomcat().getRemoteip();
|
||||
Remoteip remoteIpProperties = this.tomcatProperties.getRemoteip();
|
||||
String protocolHeader = remoteIpProperties.getProtocolHeader();
|
||||
String remoteIpHeader = remoteIpProperties.getRemoteIpHeader();
|
||||
// For back compatibility the valve is also enabled if protocol-header is set
|
||||
|
@ -320,10 +323,9 @@ public class TomcatWebServerFactoryCustomizer
|
|||
}
|
||||
|
||||
private void customizeAccessLog(ConfigurableTomcatWebServerFactory factory) {
|
||||
ServerProperties.Tomcat tomcatProperties = this.serverProperties.getTomcat();
|
||||
AccessLogValve valve = new AccessLogValve();
|
||||
PropertyMapper map = PropertyMapper.get();
|
||||
Accesslog accessLogConfig = tomcatProperties.getAccesslog();
|
||||
Accesslog accessLogConfig = this.tomcatProperties.getAccesslog();
|
||||
map.from(accessLogConfig.getConditionIf()).to(valve::setConditionIf);
|
||||
map.from(accessLogConfig.getConditionUnless()).to(valve::setConditionUnless);
|
||||
map.from(accessLogConfig.getPattern()).to(valve::setPattern);
|
||||
|
@ -344,7 +346,7 @@ public class TomcatWebServerFactoryCustomizer
|
|||
}
|
||||
|
||||
private void customizeStaticResources(ConfigurableTomcatWebServerFactory factory) {
|
||||
ServerProperties.Tomcat.Resource resource = this.serverProperties.getTomcat().getResource();
|
||||
TomcatServerProperties.Resource resource = this.tomcatProperties.getResource();
|
||||
factory.addContextCustomizers((context) -> context.addLifecycleListener((event) -> {
|
||||
if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
|
||||
context.getResources().setCachingAllowed(resource.isAllowCaching());
|
||||
|
|
|
@ -0,0 +1,425 @@
|
|||
/*
|
||||
* 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.server.undertow;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import io.undertow.UndertowOptions;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.DeprecatedConfigurationProperty;
|
||||
import org.springframework.util.unit.DataSize;
|
||||
|
||||
/**
|
||||
* Undertow server properties.
|
||||
*
|
||||
* @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 3.5.0
|
||||
*/
|
||||
@ConfigurationProperties("server.undertow")
|
||||
public class UndertowServerProperties {
|
||||
|
||||
/**
|
||||
* Maximum size of the HTTP post content. When the value is -1, the default, the size
|
||||
* is unlimited.
|
||||
*/
|
||||
private DataSize maxHttpPostSize = DataSize.ofBytes(-1);
|
||||
|
||||
/**
|
||||
* Size of each buffer. The default is derived from the maximum amount of memory that
|
||||
* is available to the JVM.
|
||||
*/
|
||||
private DataSize bufferSize;
|
||||
|
||||
/**
|
||||
* Whether to allocate buffers outside the Java heap. The default is derived from the
|
||||
* maximum amount of memory that is available to the JVM.
|
||||
*/
|
||||
private Boolean directBuffers;
|
||||
|
||||
/**
|
||||
* Whether servlet filters should be initialized on startup.
|
||||
*/
|
||||
private boolean eagerFilterInit = true;
|
||||
|
||||
/**
|
||||
* Maximum number of query or path parameters that are allowed. This limit exists to
|
||||
* prevent hash collision based DOS attacks.
|
||||
*/
|
||||
private int maxParameters = UndertowOptions.DEFAULT_MAX_PARAMETERS;
|
||||
|
||||
/**
|
||||
* Maximum number of headers that are allowed. This limit exists to prevent hash
|
||||
* collision based DOS attacks.
|
||||
*/
|
||||
private int maxHeaders = UndertowOptions.DEFAULT_MAX_HEADERS;
|
||||
|
||||
/**
|
||||
* Maximum number of cookies that are allowed. This limit exists to prevent hash
|
||||
* collision based DOS attacks.
|
||||
*/
|
||||
private int maxCookies = 200;
|
||||
|
||||
/**
|
||||
* Whether the server should decode percent encoded slash characters. Enabling encoded
|
||||
* slashes can have security implications due to different servers interpreting the
|
||||
* slash differently. Only enable this if you have a legacy application that requires
|
||||
* it. Has no effect when server.undertow.decode-slash is set.
|
||||
*/
|
||||
private boolean allowEncodedSlash = false;
|
||||
|
||||
/**
|
||||
* Whether encoded slash characters (%2F) should be decoded. Decoding can cause
|
||||
* security problems if a front-end proxy does not perform the same decoding. Only
|
||||
* enable this if you have a legacy application that requires it. When set,
|
||||
* server.undertow.allow-encoded-slash has no effect.
|
||||
*/
|
||||
private Boolean decodeSlash;
|
||||
|
||||
/**
|
||||
* Whether the URL should be decoded. When disabled, percent-encoded characters in the
|
||||
* URL will be left as-is.
|
||||
*/
|
||||
private boolean decodeUrl = true;
|
||||
|
||||
/**
|
||||
* Charset used to decode URLs.
|
||||
*/
|
||||
private Charset urlCharset = StandardCharsets.UTF_8;
|
||||
|
||||
/**
|
||||
* Whether the 'Connection: keep-alive' header should be added to all responses, even
|
||||
* if not required by the HTTP specification.
|
||||
*/
|
||||
private boolean alwaysSetKeepAlive = true;
|
||||
|
||||
/**
|
||||
* Amount of time a connection can sit idle without processing a request, before it is
|
||||
* closed by the server.
|
||||
*/
|
||||
private Duration noRequestTimeout;
|
||||
|
||||
/**
|
||||
* Whether to preserve the path of a request when it is forwarded.
|
||||
*/
|
||||
private boolean preservePathOnForward = false;
|
||||
|
||||
private final Accesslog accesslog = new Accesslog();
|
||||
|
||||
/**
|
||||
* Thread related configuration.
|
||||
*/
|
||||
private final Threads threads = new Threads();
|
||||
|
||||
private final Options options = new Options();
|
||||
|
||||
public DataSize getMaxHttpPostSize() {
|
||||
return this.maxHttpPostSize;
|
||||
}
|
||||
|
||||
public void setMaxHttpPostSize(DataSize maxHttpPostSize) {
|
||||
this.maxHttpPostSize = maxHttpPostSize;
|
||||
}
|
||||
|
||||
public DataSize getBufferSize() {
|
||||
return this.bufferSize;
|
||||
}
|
||||
|
||||
public void setBufferSize(DataSize bufferSize) {
|
||||
this.bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
public Boolean getDirectBuffers() {
|
||||
return this.directBuffers;
|
||||
}
|
||||
|
||||
public void setDirectBuffers(Boolean directBuffers) {
|
||||
this.directBuffers = directBuffers;
|
||||
}
|
||||
|
||||
public boolean isEagerFilterInit() {
|
||||
return this.eagerFilterInit;
|
||||
}
|
||||
|
||||
public void setEagerFilterInit(boolean eagerFilterInit) {
|
||||
this.eagerFilterInit = eagerFilterInit;
|
||||
}
|
||||
|
||||
public int getMaxParameters() {
|
||||
return this.maxParameters;
|
||||
}
|
||||
|
||||
public void setMaxParameters(Integer maxParameters) {
|
||||
this.maxParameters = maxParameters;
|
||||
}
|
||||
|
||||
public int getMaxHeaders() {
|
||||
return this.maxHeaders;
|
||||
}
|
||||
|
||||
public void setMaxHeaders(int maxHeaders) {
|
||||
this.maxHeaders = maxHeaders;
|
||||
}
|
||||
|
||||
public Integer getMaxCookies() {
|
||||
return this.maxCookies;
|
||||
}
|
||||
|
||||
public void setMaxCookies(Integer maxCookies) {
|
||||
this.maxCookies = maxCookies;
|
||||
}
|
||||
|
||||
@DeprecatedConfigurationProperty(replacement = "server.undertow.decode-slash", since = "3.0.3")
|
||||
@Deprecated(forRemoval = true, since = "3.0.3")
|
||||
public boolean isAllowEncodedSlash() {
|
||||
return this.allowEncodedSlash;
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true, since = "3.0.3")
|
||||
public void setAllowEncodedSlash(boolean allowEncodedSlash) {
|
||||
this.allowEncodedSlash = allowEncodedSlash;
|
||||
}
|
||||
|
||||
public Boolean getDecodeSlash() {
|
||||
return this.decodeSlash;
|
||||
}
|
||||
|
||||
public void setDecodeSlash(Boolean decodeSlash) {
|
||||
this.decodeSlash = decodeSlash;
|
||||
}
|
||||
|
||||
public boolean isDecodeUrl() {
|
||||
return this.decodeUrl;
|
||||
}
|
||||
|
||||
public void setDecodeUrl(Boolean decodeUrl) {
|
||||
this.decodeUrl = decodeUrl;
|
||||
}
|
||||
|
||||
public Charset getUrlCharset() {
|
||||
return this.urlCharset;
|
||||
}
|
||||
|
||||
public void setUrlCharset(Charset urlCharset) {
|
||||
this.urlCharset = urlCharset;
|
||||
}
|
||||
|
||||
public boolean isAlwaysSetKeepAlive() {
|
||||
return this.alwaysSetKeepAlive;
|
||||
}
|
||||
|
||||
public void setAlwaysSetKeepAlive(boolean alwaysSetKeepAlive) {
|
||||
this.alwaysSetKeepAlive = alwaysSetKeepAlive;
|
||||
}
|
||||
|
||||
public Duration getNoRequestTimeout() {
|
||||
return this.noRequestTimeout;
|
||||
}
|
||||
|
||||
public void setNoRequestTimeout(Duration noRequestTimeout) {
|
||||
this.noRequestTimeout = noRequestTimeout;
|
||||
}
|
||||
|
||||
public boolean isPreservePathOnForward() {
|
||||
return this.preservePathOnForward;
|
||||
}
|
||||
|
||||
public void setPreservePathOnForward(boolean preservePathOnForward) {
|
||||
this.preservePathOnForward = preservePathOnForward;
|
||||
}
|
||||
|
||||
public UndertowServerProperties.Accesslog getAccesslog() {
|
||||
return this.accesslog;
|
||||
}
|
||||
|
||||
public UndertowServerProperties.Threads getThreads() {
|
||||
return this.threads;
|
||||
}
|
||||
|
||||
public UndertowServerProperties.Options getOptions() {
|
||||
return this.options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Undertow access log properties.
|
||||
*/
|
||||
public static class Accesslog {
|
||||
|
||||
/**
|
||||
* Whether to enable the access log.
|
||||
*/
|
||||
private boolean enabled = false;
|
||||
|
||||
/**
|
||||
* Format pattern for access logs.
|
||||
*/
|
||||
private String pattern = "common";
|
||||
|
||||
/**
|
||||
* Log file name prefix.
|
||||
*/
|
||||
protected String prefix = "access_log.";
|
||||
|
||||
/**
|
||||
* Log file name suffix.
|
||||
*/
|
||||
private String suffix = "log";
|
||||
|
||||
/**
|
||||
* Undertow access log directory.
|
||||
*/
|
||||
private File dir = new File("logs");
|
||||
|
||||
/**
|
||||
* Whether to enable access log rotation.
|
||||
*/
|
||||
private boolean rotate = true;
|
||||
|
||||
public boolean isEnabled() {
|
||||
return this.enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getPattern() {
|
||||
return this.pattern;
|
||||
}
|
||||
|
||||
public void setPattern(String pattern) {
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
return this.prefix;
|
||||
}
|
||||
|
||||
public void setPrefix(String prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
public String getSuffix() {
|
||||
return this.suffix;
|
||||
}
|
||||
|
||||
public void setSuffix(String suffix) {
|
||||
this.suffix = suffix;
|
||||
}
|
||||
|
||||
public File getDir() {
|
||||
return this.dir;
|
||||
}
|
||||
|
||||
public void setDir(File dir) {
|
||||
this.dir = dir;
|
||||
}
|
||||
|
||||
public boolean isRotate() {
|
||||
return this.rotate;
|
||||
}
|
||||
|
||||
public void setRotate(boolean rotate) {
|
||||
this.rotate = rotate;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Undertow thread properties.
|
||||
*/
|
||||
public static class Threads {
|
||||
|
||||
/**
|
||||
* Number of I/O threads to create for the worker. The default is derived from the
|
||||
* number of available processors.
|
||||
*/
|
||||
private Integer io;
|
||||
|
||||
/**
|
||||
* Number of worker threads. The default is 8 times the number of I/O threads.
|
||||
*/
|
||||
private Integer worker;
|
||||
|
||||
public Integer getIo() {
|
||||
return this.io;
|
||||
}
|
||||
|
||||
public void setIo(Integer io) {
|
||||
this.io = io;
|
||||
}
|
||||
|
||||
public Integer getWorker() {
|
||||
return this.worker;
|
||||
}
|
||||
|
||||
public void setWorker(Integer worker) {
|
||||
this.worker = worker;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class Options {
|
||||
|
||||
/**
|
||||
* Socket options as defined in org.xnio.Options.
|
||||
*/
|
||||
private final Map<String, String> socket = new LinkedHashMap<>();
|
||||
|
||||
/**
|
||||
* Server options as defined in io.undertow.UndertowOptions.
|
||||
*/
|
||||
private final Map<String, String> server = new LinkedHashMap<>();
|
||||
|
||||
public Map<String, String> getServer() {
|
||||
return this.server;
|
||||
}
|
||||
|
||||
public Map<String, String> getSocket() {
|
||||
return this.socket;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -39,8 +39,8 @@ public class UndertowWebServerConfiguration {
|
|||
|
||||
@Bean
|
||||
UndertowWebServerFactoryCustomizer undertowWebServerFactoryCustomizer(Environment environment,
|
||||
ServerProperties serverProperties) {
|
||||
return new UndertowWebServerFactoryCustomizer(environment, serverProperties);
|
||||
ServerProperties serverProperties, UndertowServerProperties undertowProperties) {
|
||||
return new UndertowWebServerFactoryCustomizer(environment, serverProperties, undertowProperties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
|
|
@ -30,8 +30,7 @@ import org.xnio.Option;
|
|||
import org.xnio.Options;
|
||||
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties.Undertow;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties.Undertow.Accesslog;
|
||||
import org.springframework.boot.autoconfigure.web.server.undertow.UndertowServerProperties.Accesslog;
|
||||
import org.springframework.boot.cloud.CloudPlatform;
|
||||
import org.springframework.boot.context.properties.PropertyMapper;
|
||||
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
|
||||
|
@ -63,9 +62,13 @@ public class UndertowWebServerFactoryCustomizer
|
|||
|
||||
private final ServerProperties serverProperties;
|
||||
|
||||
public UndertowWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {
|
||||
private final UndertowServerProperties undertowProperties;
|
||||
|
||||
public UndertowWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties,
|
||||
UndertowServerProperties undertowProperties) {
|
||||
this.environment = environment;
|
||||
this.serverProperties = serverProperties;
|
||||
this.undertowProperties = undertowProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -88,33 +91,38 @@ public class UndertowWebServerFactoryCustomizer
|
|||
|
||||
private void mapUndertowProperties(ConfigurableUndertowWebServerFactory factory, ServerOptions serverOptions) {
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
Undertow properties = this.serverProperties.getUndertow();
|
||||
map.from(properties::getBufferSize).whenNonNull().asInt(DataSize::toBytes).to(factory::setBufferSize);
|
||||
ServerProperties.Undertow.Threads threadProperties = properties.getThreads();
|
||||
map.from(this.undertowProperties::getBufferSize)
|
||||
.whenNonNull()
|
||||
.asInt(DataSize::toBytes)
|
||||
.to(factory::setBufferSize);
|
||||
UndertowServerProperties.Threads threadProperties = this.undertowProperties.getThreads();
|
||||
map.from(threadProperties::getIo).to(factory::setIoThreads);
|
||||
map.from(threadProperties::getWorker).to(factory::setWorkerThreads);
|
||||
map.from(properties::getDirectBuffers).to(factory::setUseDirectBuffers);
|
||||
map.from(properties::getMaxHttpPostSize)
|
||||
map.from(this.undertowProperties::getDirectBuffers).to(factory::setUseDirectBuffers);
|
||||
map.from(this.undertowProperties::getMaxHttpPostSize)
|
||||
.as(DataSize::toBytes)
|
||||
.when(this::isPositive)
|
||||
.to(serverOptions.option(UndertowOptions.MAX_ENTITY_SIZE));
|
||||
map.from(properties::getMaxParameters).to(serverOptions.option(UndertowOptions.MAX_PARAMETERS));
|
||||
map.from(properties::getMaxHeaders).to(serverOptions.option(UndertowOptions.MAX_HEADERS));
|
||||
map.from(properties::getMaxCookies).to(serverOptions.option(UndertowOptions.MAX_COOKIES));
|
||||
mapSlashProperties(properties, serverOptions);
|
||||
map.from(properties::isDecodeUrl).to(serverOptions.option(UndertowOptions.DECODE_URL));
|
||||
map.from(properties::getUrlCharset).as(Charset::name).to(serverOptions.option(UndertowOptions.URL_CHARSET));
|
||||
map.from(properties::isAlwaysSetKeepAlive).to(serverOptions.option(UndertowOptions.ALWAYS_SET_KEEP_ALIVE));
|
||||
map.from(properties::getNoRequestTimeout)
|
||||
map.from(this.undertowProperties::getMaxParameters).to(serverOptions.option(UndertowOptions.MAX_PARAMETERS));
|
||||
map.from(this.undertowProperties::getMaxHeaders).to(serverOptions.option(UndertowOptions.MAX_HEADERS));
|
||||
map.from(this.undertowProperties::getMaxCookies).to(serverOptions.option(UndertowOptions.MAX_COOKIES));
|
||||
mapSlashProperties(this.undertowProperties, serverOptions);
|
||||
map.from(this.undertowProperties::isDecodeUrl).to(serverOptions.option(UndertowOptions.DECODE_URL));
|
||||
map.from(this.undertowProperties::getUrlCharset)
|
||||
.as(Charset::name)
|
||||
.to(serverOptions.option(UndertowOptions.URL_CHARSET));
|
||||
map.from(this.undertowProperties::isAlwaysSetKeepAlive)
|
||||
.to(serverOptions.option(UndertowOptions.ALWAYS_SET_KEEP_ALIVE));
|
||||
map.from(this.undertowProperties::getNoRequestTimeout)
|
||||
.asInt(Duration::toMillis)
|
||||
.to(serverOptions.option(UndertowOptions.NO_REQUEST_TIMEOUT));
|
||||
map.from(properties.getOptions()::getServer).to(serverOptions.forEach(serverOptions::option));
|
||||
map.from(this.undertowProperties.getOptions()::getServer).to(serverOptions.forEach(serverOptions::option));
|
||||
SocketOptions socketOptions = new SocketOptions(factory);
|
||||
map.from(properties.getOptions()::getSocket).to(socketOptions.forEach(socketOptions::option));
|
||||
map.from(this.undertowProperties.getOptions()::getSocket).to(socketOptions.forEach(socketOptions::option));
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "deprecation", "removal" })
|
||||
private void mapSlashProperties(Undertow properties, ServerOptions serverOptions) {
|
||||
private void mapSlashProperties(UndertowServerProperties properties, ServerOptions serverOptions) {
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
map.from(properties::isAllowEncodedSlash).to(serverOptions.option(UndertowOptions.ALLOW_ENCODED_SLASH));
|
||||
map.from(properties::getDecodeSlash).to(serverOptions.option(UndertowOptions.DECODE_SLASH));
|
||||
|
@ -126,7 +134,7 @@ public class UndertowWebServerFactoryCustomizer
|
|||
}
|
||||
|
||||
private void mapAccessLogProperties(ConfigurableUndertowWebServerFactory factory) {
|
||||
Accesslog properties = this.serverProperties.getUndertow().getAccesslog();
|
||||
Accesslog properties = this.undertowProperties.getAccesslog();
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
map.from(properties::isEnabled).to(factory::setAccessLogEnabled);
|
||||
map.from(properties::getDir).to(factory::setAccessLogDirectory);
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* 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.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.web.server.jetty.JettyServerProperties;
|
||||
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.jetty.JettyWebServer;
|
||||
import org.springframework.boot.web.server.servlet.jetty.JettyServletWebServerFactory;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link JettyServerProperties}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class JettyServerPropertiesTests {
|
||||
|
||||
private final JettyServerProperties properties = new JettyServerProperties();
|
||||
|
||||
@Test
|
||||
void testCustomizeJettyAcceptors() {
|
||||
bind("server.jetty.threads.acceptors", "10");
|
||||
assertThat(this.properties.getThreads().getAcceptors()).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeJettySelectors() {
|
||||
bind("server.jetty.threads.selectors", "10");
|
||||
assertThat(this.properties.getThreads().getSelectors()).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeJettyMaxThreads() {
|
||||
bind("server.jetty.threads.max", "10");
|
||||
assertThat(this.properties.getThreads().getMax()).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeJettyMinThreads() {
|
||||
bind("server.jetty.threads.min", "10");
|
||||
assertThat(this.properties.getThreads().getMin()).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeJettyIdleTimeout() {
|
||||
bind("server.jetty.threads.idle-timeout", "10s");
|
||||
assertThat(this.properties.getThreads().getIdleTimeout()).isEqualTo(Duration.ofSeconds(10));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeJettyMaxQueueCapacity() {
|
||||
bind("server.jetty.threads.max-queue-capacity", "5150");
|
||||
assertThat(this.properties.getThreads().getMaxQueueCapacity()).isEqualTo(5150);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeJettyAccessLog() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("server.jetty.accesslog.enabled", "true");
|
||||
map.put("server.jetty.accesslog.filename", "foo.txt");
|
||||
map.put("server.jetty.accesslog.file-date-format", "yyyymmdd");
|
||||
map.put("server.jetty.accesslog.retention-period", "4");
|
||||
map.put("server.jetty.accesslog.append", "true");
|
||||
map.put("server.jetty.accesslog.custom-format", "{client}a - %u %t \"%r\" %s %O");
|
||||
map.put("server.jetty.accesslog.ignore-paths", "/a/path,/b/path");
|
||||
bind(map);
|
||||
assertThat(this.properties.getAccesslog().isEnabled()).isTrue();
|
||||
assertThat(this.properties.getAccesslog().getFilename()).isEqualTo("foo.txt");
|
||||
assertThat(this.properties.getAccesslog().getFileDateFormat()).isEqualTo("yyyymmdd");
|
||||
assertThat(this.properties.getAccesslog().getRetentionPeriod()).isEqualTo(4);
|
||||
assertThat(this.properties.getAccesslog().isAppend()).isTrue();
|
||||
assertThat(this.properties.getAccesslog().getCustomFormat()).isEqualTo("{client}a - %u %t \"%r\" %s %O");
|
||||
assertThat(this.properties.getAccesslog().getIgnorePaths()).containsExactly("/a/path", "/b/path");
|
||||
}
|
||||
|
||||
@Test
|
||||
void jettyThreadPoolPropertyDefaultsShouldMatchServerDefault() {
|
||||
JettyServletWebServerFactory jettyFactory = new JettyServletWebServerFactory(0);
|
||||
JettyWebServer jetty = (JettyWebServer) jettyFactory.getWebServer();
|
||||
Server server = jetty.getServer();
|
||||
QueuedThreadPool threadPool = (QueuedThreadPool) server.getThreadPool();
|
||||
int idleTimeout = threadPool.getIdleTimeout();
|
||||
int maxThreads = threadPool.getMaxThreads();
|
||||
int minThreads = threadPool.getMinThreads();
|
||||
assertThat(this.properties.getThreads().getIdleTimeout().toMillis()).isEqualTo(idleTimeout);
|
||||
assertThat(this.properties.getThreads().getMax()).isEqualTo(maxThreads);
|
||||
assertThat(this.properties.getThreads().getMin()).isEqualTo(minThreads);
|
||||
}
|
||||
|
||||
@Test
|
||||
void jettyMaxHttpFormPostSizeMatchesDefault() {
|
||||
JettyServletWebServerFactory jettyFactory = new JettyServletWebServerFactory(0);
|
||||
JettyWebServer jetty = (JettyWebServer) jettyFactory.getWebServer();
|
||||
Server server = jetty.getServer();
|
||||
assertThat(this.properties.getMaxHttpFormPostSize().toBytes())
|
||||
.isEqualTo(((ServletContextHandler) server.getHandler()).getMaxFormContentSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
void jettyMaxFormKeysMatchesDefault() {
|
||||
JettyServletWebServerFactory jettyFactory = new JettyServletWebServerFactory(0);
|
||||
JettyWebServer jetty = (JettyWebServer) jettyFactory.getWebServer();
|
||||
Server server = jetty.getServer();
|
||||
assertThat(this.properties.getMaxFormKeys())
|
||||
.isEqualTo(((ServletContextHandler) server.getHandler()).getMaxFormKeys());
|
||||
}
|
||||
|
||||
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.jetty", Bindable.ofInstance(this.properties));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* 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.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.netty.http.HttpDecoderSpec;
|
||||
|
||||
import org.springframework.boot.autoconfigure.web.server.reactive.netty.NettyServerProperties;
|
||||
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 static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link NettyServerProperties}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class NettyServerPropertiesTests {
|
||||
|
||||
private final NettyServerProperties properties = new NettyServerProperties();
|
||||
|
||||
@Test
|
||||
void testCustomizeNettyIdleTimeout() {
|
||||
bind("server.netty.idle-timeout", "10s");
|
||||
assertThat(this.properties.getIdleTimeout()).isEqualTo(Duration.ofSeconds(10));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeNettyMaxKeepAliveRequests() {
|
||||
bind("server.netty.max-keep-alive-requests", "100");
|
||||
assertThat(this.properties.getMaxKeepAliveRequests()).isEqualTo(100);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nettyMaxInitialLineLengthMatchesHttpDecoderSpecDefault() {
|
||||
assertThat(this.properties.getMaxInitialLineLength().toBytes())
|
||||
.isEqualTo(HttpDecoderSpec.DEFAULT_MAX_INITIAL_LINE_LENGTH);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nettyValidateHeadersMatchesHttpDecoderSpecDefault() {
|
||||
assertThat(this.properties.isValidateHeaders()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void nettyH2cMaxContentLengthMatchesHttpDecoderSpecDefault() {
|
||||
assertThat(this.properties.getH2cMaxContentLength().toBytes()).isZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
void nettyInitialBufferSizeMatchesHttpDecoderSpecDefault() {
|
||||
assertThat(this.properties.getInitialBufferSize().toBytes())
|
||||
.isEqualTo(HttpDecoderSpec.DEFAULT_INITIAL_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
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.netty", Bindable.ofInstance(this.properties));
|
||||
}
|
||||
|
||||
}
|
|
@ -17,28 +17,11 @@
|
|||
package org.springframework.boot.autoconfigure.web;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import io.undertow.UndertowOptions;
|
||||
import org.apache.catalina.connector.Connector;
|
||||
import org.apache.catalina.core.StandardContext;
|
||||
import org.apache.catalina.core.StandardEngine;
|
||||
import org.apache.catalina.valves.AccessLogValve;
|
||||
import org.apache.catalina.valves.RemoteIpValve;
|
||||
import org.apache.coyote.AbstractProtocol;
|
||||
import org.apache.tomcat.util.net.AbstractEndpoint;
|
||||
import org.eclipse.jetty.ee10.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.netty.http.HttpDecoderSpec;
|
||||
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties.Tomcat.Accesslog;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties.Tomcat.UseApr;
|
||||
import org.springframework.boot.context.properties.bind.Bindable;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
|
||||
|
@ -46,10 +29,6 @@ import org.springframework.boot.context.properties.source.MapConfigurationProper
|
|||
import org.springframework.boot.testsupport.web.servlet.DirtiesUrlFactories;
|
||||
import org.springframework.boot.web.server.MimeMappings;
|
||||
import org.springframework.boot.web.server.MimeMappings.Mapping;
|
||||
import org.springframework.boot.web.server.jetty.JettyWebServer;
|
||||
import org.springframework.boot.web.server.servlet.jetty.JettyServletWebServerFactory;
|
||||
import org.springframework.boot.web.server.servlet.tomcat.TomcatServletWebServerFactory;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import org.springframework.util.unit.DataSize;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
@ -100,56 +79,6 @@ class ServerPropertiesTests {
|
|||
assertThat(this.properties.getServerHeader()).isEqualTo("Custom Server");
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("removal")
|
||||
void testTomcatBinding() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("server.tomcat.accesslog.conditionIf", "foo");
|
||||
map.put("server.tomcat.accesslog.conditionUnless", "bar");
|
||||
map.put("server.tomcat.accesslog.pattern", "%h %t '%r' %s %b");
|
||||
map.put("server.tomcat.accesslog.prefix", "foo");
|
||||
map.put("server.tomcat.accesslog.suffix", "-bar.log");
|
||||
map.put("server.tomcat.accesslog.encoding", "UTF-8");
|
||||
map.put("server.tomcat.accesslog.locale", "en-AU");
|
||||
map.put("server.tomcat.accesslog.checkExists", "true");
|
||||
map.put("server.tomcat.accesslog.rotate", "false");
|
||||
map.put("server.tomcat.accesslog.rename-on-rotate", "true");
|
||||
map.put("server.tomcat.accesslog.ipv6Canonical", "true");
|
||||
map.put("server.tomcat.accesslog.request-attributes-enabled", "true");
|
||||
map.put("server.tomcat.remoteip.protocol-header", "X-Forwarded-Protocol");
|
||||
map.put("server.tomcat.remoteip.remote-ip-header", "Remote-Ip");
|
||||
map.put("server.tomcat.remoteip.internal-proxies", "10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}");
|
||||
map.put("server.tomcat.remoteip.trusted-proxies", "proxy1|proxy2|proxy3");
|
||||
map.put("server.tomcat.reject-illegal-header", "false");
|
||||
map.put("server.tomcat.background-processor-delay", "10");
|
||||
map.put("server.tomcat.relaxed-path-chars", "|,<");
|
||||
map.put("server.tomcat.relaxed-query-chars", "^ , | ");
|
||||
map.put("server.tomcat.use-relative-redirects", "true");
|
||||
bind(map);
|
||||
ServerProperties.Tomcat tomcat = this.properties.getTomcat();
|
||||
Accesslog accesslog = tomcat.getAccesslog();
|
||||
assertThat(accesslog.getConditionIf()).isEqualTo("foo");
|
||||
assertThat(accesslog.getConditionUnless()).isEqualTo("bar");
|
||||
assertThat(accesslog.getPattern()).isEqualTo("%h %t '%r' %s %b");
|
||||
assertThat(accesslog.getPrefix()).isEqualTo("foo");
|
||||
assertThat(accesslog.getSuffix()).isEqualTo("-bar.log");
|
||||
assertThat(accesslog.getEncoding()).isEqualTo("UTF-8");
|
||||
assertThat(accesslog.getLocale()).isEqualTo("en-AU");
|
||||
assertThat(accesslog.isCheckExists()).isTrue();
|
||||
assertThat(accesslog.isRotate()).isFalse();
|
||||
assertThat(accesslog.isRenameOnRotate()).isTrue();
|
||||
assertThat(accesslog.isIpv6Canonical()).isTrue();
|
||||
assertThat(accesslog.isRequestAttributesEnabled()).isTrue();
|
||||
assertThat(tomcat.getRemoteip().getRemoteIpHeader()).isEqualTo("Remote-Ip");
|
||||
assertThat(tomcat.getRemoteip().getProtocolHeader()).isEqualTo("X-Forwarded-Protocol");
|
||||
assertThat(tomcat.getRemoteip().getInternalProxies()).isEqualTo("10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}");
|
||||
assertThat(tomcat.getRemoteip().getTrustedProxies()).isEqualTo("proxy1|proxy2|proxy3");
|
||||
assertThat(tomcat.getBackgroundProcessorDelay()).hasSeconds(10);
|
||||
assertThat(tomcat.getRelaxedPathChars()).containsExactly('|', '<');
|
||||
assertThat(tomcat.getRelaxedQueryChars()).containsExactly('^', '|');
|
||||
assertThat(tomcat.isUseRelativeRedirects()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTrailingSlashOfContextPathIsRemoved() {
|
||||
bind("server.servlet.context-path", "/foo/");
|
||||
|
@ -200,12 +129,6 @@ class ServerPropertiesTests {
|
|||
.containsExactly(expectedMappings.getAll().toArray(new Mapping[0]));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeTomcatUriEncoding() {
|
||||
bind("server.tomcat.uri-encoding", "US-ASCII");
|
||||
assertThat(this.properties.getTomcat().getUriEncoding()).isEqualTo(StandardCharsets.US_ASCII);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeMaxHttpRequestHeaderSize() {
|
||||
bind("server.max-http-request-header-size", "1MB");
|
||||
|
@ -218,311 +141,6 @@ class ServerPropertiesTests {
|
|||
assertThat(this.properties.getMaxHttpRequestHeaderSize()).isEqualTo(DataSize.ofKilobytes(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeTomcatMaxThreads() {
|
||||
bind("server.tomcat.threads.max", "10");
|
||||
assertThat(this.properties.getTomcat().getThreads().getMax()).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeTomcatKeepAliveTimeout() {
|
||||
bind("server.tomcat.keep-alive-timeout", "30s");
|
||||
assertThat(this.properties.getTomcat().getKeepAliveTimeout()).hasSeconds(30);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeTomcatKeepAliveTimeoutWithInfinite() {
|
||||
bind("server.tomcat.keep-alive-timeout", "-1");
|
||||
assertThat(this.properties.getTomcat().getKeepAliveTimeout()).hasMillis(-1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeTomcatMaxKeepAliveRequests() {
|
||||
bind("server.tomcat.max-keep-alive-requests", "200");
|
||||
assertThat(this.properties.getTomcat().getMaxKeepAliveRequests()).isEqualTo(200);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeTomcatMaxKeepAliveRequestsWithInfinite() {
|
||||
bind("server.tomcat.max-keep-alive-requests", "-1");
|
||||
assertThat(this.properties.getTomcat().getMaxKeepAliveRequests()).isEqualTo(-1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeTomcatMaxParameterCount() {
|
||||
bind("server.tomcat.max-parameter-count", "100");
|
||||
assertThat(this.properties.getTomcat().getMaxParameterCount()).isEqualTo(100);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeTomcatMinSpareThreads() {
|
||||
bind("server.tomcat.threads.min-spare", "10");
|
||||
assertThat(this.properties.getTomcat().getThreads().getMinSpare()).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeJettyAcceptors() {
|
||||
bind("server.jetty.threads.acceptors", "10");
|
||||
assertThat(this.properties.getJetty().getThreads().getAcceptors()).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeJettySelectors() {
|
||||
bind("server.jetty.threads.selectors", "10");
|
||||
assertThat(this.properties.getJetty().getThreads().getSelectors()).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeJettyMaxThreads() {
|
||||
bind("server.jetty.threads.max", "10");
|
||||
assertThat(this.properties.getJetty().getThreads().getMax()).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeJettyMinThreads() {
|
||||
bind("server.jetty.threads.min", "10");
|
||||
assertThat(this.properties.getJetty().getThreads().getMin()).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeJettyIdleTimeout() {
|
||||
bind("server.jetty.threads.idle-timeout", "10s");
|
||||
assertThat(this.properties.getJetty().getThreads().getIdleTimeout()).isEqualTo(Duration.ofSeconds(10));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeJettyMaxQueueCapacity() {
|
||||
bind("server.jetty.threads.max-queue-capacity", "5150");
|
||||
assertThat(this.properties.getJetty().getThreads().getMaxQueueCapacity()).isEqualTo(5150);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeUndertowServerOption() {
|
||||
bind("server.undertow.options.server.ALWAYS_SET_KEEP_ALIVE", "true");
|
||||
assertThat(this.properties.getUndertow().getOptions().getServer()).containsEntry("ALWAYS_SET_KEEP_ALIVE",
|
||||
"true");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeUndertowSocketOption() {
|
||||
bind("server.undertow.options.socket.ALWAYS_SET_KEEP_ALIVE", "true");
|
||||
assertThat(this.properties.getUndertow().getOptions().getSocket()).containsEntry("ALWAYS_SET_KEEP_ALIVE",
|
||||
"true");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeUndertowIoThreads() {
|
||||
bind("server.undertow.threads.io", "4");
|
||||
assertThat(this.properties.getUndertow().getThreads().getIo()).isEqualTo(4);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeUndertowWorkerThreads() {
|
||||
bind("server.undertow.threads.worker", "10");
|
||||
assertThat(this.properties.getUndertow().getThreads().getWorker()).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeJettyAccessLog() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("server.jetty.accesslog.enabled", "true");
|
||||
map.put("server.jetty.accesslog.filename", "foo.txt");
|
||||
map.put("server.jetty.accesslog.file-date-format", "yyyymmdd");
|
||||
map.put("server.jetty.accesslog.retention-period", "4");
|
||||
map.put("server.jetty.accesslog.append", "true");
|
||||
map.put("server.jetty.accesslog.custom-format", "{client}a - %u %t \"%r\" %s %O");
|
||||
map.put("server.jetty.accesslog.ignore-paths", "/a/path,/b/path");
|
||||
bind(map);
|
||||
ServerProperties.Jetty jetty = this.properties.getJetty();
|
||||
assertThat(jetty.getAccesslog().isEnabled()).isTrue();
|
||||
assertThat(jetty.getAccesslog().getFilename()).isEqualTo("foo.txt");
|
||||
assertThat(jetty.getAccesslog().getFileDateFormat()).isEqualTo("yyyymmdd");
|
||||
assertThat(jetty.getAccesslog().getRetentionPeriod()).isEqualTo(4);
|
||||
assertThat(jetty.getAccesslog().isAppend()).isTrue();
|
||||
assertThat(jetty.getAccesslog().getCustomFormat()).isEqualTo("{client}a - %u %t \"%r\" %s %O");
|
||||
assertThat(jetty.getAccesslog().getIgnorePaths()).containsExactly("/a/path", "/b/path");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeNettyIdleTimeout() {
|
||||
bind("server.netty.idle-timeout", "10s");
|
||||
assertThat(this.properties.getNetty().getIdleTimeout()).isEqualTo(Duration.ofSeconds(10));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeNettyMaxKeepAliveRequests() {
|
||||
bind("server.netty.max-keep-alive-requests", "100");
|
||||
assertThat(this.properties.getNetty().getMaxKeepAliveRequests()).isEqualTo(100);
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatAcceptCountMatchesProtocolDefault() throws Exception {
|
||||
assertThat(this.properties.getTomcat().getAcceptCount()).isEqualTo(getDefaultProtocol().getAcceptCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatProcessorCacheMatchesProtocolDefault() throws Exception {
|
||||
assertThat(this.properties.getTomcat().getProcessorCache()).isEqualTo(getDefaultProtocol().getProcessorCache());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatMaxConnectionsMatchesProtocolDefault() throws Exception {
|
||||
assertThat(this.properties.getTomcat().getMaxConnections()).isEqualTo(getDefaultProtocol().getMaxConnections());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatMaxThreadsMatchesProtocolDefault() throws Exception {
|
||||
assertThat(this.properties.getTomcat().getThreads().getMax()).isEqualTo(getDefaultProtocol().getMaxThreads());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatMinSpareThreadsMatchesProtocolDefault() throws Exception {
|
||||
assertThat(this.properties.getTomcat().getThreads().getMinSpare())
|
||||
.isEqualTo(getDefaultProtocol().getMinSpareThreads());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatMaxHttpPostSizeMatchesConnectorDefault() {
|
||||
assertThat(this.properties.getTomcat().getMaxHttpFormPostSize().toBytes())
|
||||
.isEqualTo(getDefaultConnector().getMaxPostSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatMaxParameterCountMatchesConnectorDefault() {
|
||||
assertThat(this.properties.getTomcat().getMaxParameterCount())
|
||||
.isEqualTo(getDefaultConnector().getMaxParameterCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatBackgroundProcessorDelayMatchesEngineDefault() {
|
||||
assertThat(this.properties.getTomcat().getBackgroundProcessorDelay())
|
||||
.hasSeconds((new StandardEngine().getBackgroundProcessorDelay()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatMaxHttpFormPostSizeMatchesConnectorDefault() {
|
||||
assertThat(this.properties.getTomcat().getMaxHttpFormPostSize().toBytes())
|
||||
.isEqualTo(getDefaultConnector().getMaxPostSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatUriEncodingMatchesConnectorDefault() {
|
||||
assertThat(this.properties.getTomcat().getUriEncoding().name())
|
||||
.isEqualTo(getDefaultConnector().getURIEncoding());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatRedirectContextRootMatchesDefault() {
|
||||
assertThat(this.properties.getTomcat().getRedirectContextRoot())
|
||||
.isEqualTo(new StandardContext().getMapperContextRootRedirectEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatAccessLogRenameOnRotateMatchesDefault() {
|
||||
assertThat(this.properties.getTomcat().getAccesslog().isRenameOnRotate())
|
||||
.isEqualTo(new AccessLogValve().isRenameOnRotate());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatAccessLogRequestAttributesEnabledMatchesDefault() {
|
||||
assertThat(this.properties.getTomcat().getAccesslog().isRequestAttributesEnabled())
|
||||
.isEqualTo(new AccessLogValve().getRequestAttributesEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatInternalProxiesMatchesDefault() {
|
||||
assertThat(this.properties.getTomcat().getRemoteip().getInternalProxies())
|
||||
.isEqualTo(new RemoteIpValve().getInternalProxies());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatUseRelativeRedirectsDefaultsToFalse() {
|
||||
assertThat(this.properties.getTomcat().isUseRelativeRedirects()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatMaxKeepAliveRequestsDefault() throws Exception {
|
||||
AbstractEndpoint<?, ?> endpoint = (AbstractEndpoint<?, ?>) ReflectionTestUtils.getField(getDefaultProtocol(),
|
||||
"endpoint");
|
||||
int defaultMaxKeepAliveRequests = (int) ReflectionTestUtils.getField(endpoint, "maxKeepAliveRequests");
|
||||
assertThat(this.properties.getTomcat().getMaxKeepAliveRequests()).isEqualTo(defaultMaxKeepAliveRequests);
|
||||
}
|
||||
|
||||
@Test
|
||||
void jettyThreadPoolPropertyDefaultsShouldMatchServerDefault() {
|
||||
JettyServletWebServerFactory jettyFactory = new JettyServletWebServerFactory(0);
|
||||
JettyWebServer jetty = (JettyWebServer) jettyFactory.getWebServer();
|
||||
Server server = jetty.getServer();
|
||||
QueuedThreadPool threadPool = (QueuedThreadPool) server.getThreadPool();
|
||||
int idleTimeout = threadPool.getIdleTimeout();
|
||||
int maxThreads = threadPool.getMaxThreads();
|
||||
int minThreads = threadPool.getMinThreads();
|
||||
assertThat(this.properties.getJetty().getThreads().getIdleTimeout().toMillis()).isEqualTo(idleTimeout);
|
||||
assertThat(this.properties.getJetty().getThreads().getMax()).isEqualTo(maxThreads);
|
||||
assertThat(this.properties.getJetty().getThreads().getMin()).isEqualTo(minThreads);
|
||||
}
|
||||
|
||||
@Test
|
||||
void jettyMaxHttpFormPostSizeMatchesDefault() {
|
||||
JettyServletWebServerFactory jettyFactory = new JettyServletWebServerFactory(0);
|
||||
JettyWebServer jetty = (JettyWebServer) jettyFactory.getWebServer();
|
||||
Server server = jetty.getServer();
|
||||
assertThat(this.properties.getJetty().getMaxHttpFormPostSize().toBytes())
|
||||
.isEqualTo(((ServletContextHandler) server.getHandler()).getMaxFormContentSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
void jettyMaxFormKeysMatchesDefault() {
|
||||
JettyServletWebServerFactory jettyFactory = new JettyServletWebServerFactory(0);
|
||||
JettyWebServer jetty = (JettyWebServer) jettyFactory.getWebServer();
|
||||
Server server = jetty.getServer();
|
||||
assertThat(this.properties.getJetty().getMaxFormKeys())
|
||||
.isEqualTo(((ServletContextHandler) server.getHandler()).getMaxFormKeys());
|
||||
}
|
||||
|
||||
@Test
|
||||
void undertowMaxHttpPostSizeMatchesDefault() {
|
||||
assertThat(this.properties.getUndertow().getMaxHttpPostSize().toBytes())
|
||||
.isEqualTo(UndertowOptions.DEFAULT_MAX_ENTITY_SIZE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nettyMaxInitialLineLengthMatchesHttpDecoderSpecDefault() {
|
||||
assertThat(this.properties.getNetty().getMaxInitialLineLength().toBytes())
|
||||
.isEqualTo(HttpDecoderSpec.DEFAULT_MAX_INITIAL_LINE_LENGTH);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nettyValidateHeadersMatchesHttpDecoderSpecDefault() {
|
||||
assertThat(this.properties.getNetty().isValidateHeaders()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void nettyH2cMaxContentLengthMatchesHttpDecoderSpecDefault() {
|
||||
assertThat(this.properties.getNetty().getH2cMaxContentLength().toBytes()).isZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
void nettyInitialBufferSizeMatchesHttpDecoderSpecDefault() {
|
||||
assertThat(this.properties.getNetty().getInitialBufferSize().toBytes())
|
||||
.isEqualTo(HttpDecoderSpec.DEFAULT_INITIAL_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldDefaultAprToNever() {
|
||||
assertThat(this.properties.getTomcat().getUseApr()).isEqualTo(UseApr.NEVER);
|
||||
}
|
||||
|
||||
private Connector getDefaultConnector() {
|
||||
return new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
|
||||
}
|
||||
|
||||
private AbstractProtocol<?> getDefaultProtocol() throws Exception {
|
||||
return (AbstractProtocol<?>) Class.forName(TomcatServletWebServerFactory.DEFAULT_PROTOCOL)
|
||||
.getDeclaredConstructor()
|
||||
.newInstance();
|
||||
}
|
||||
|
||||
private void bind(String name, String value) {
|
||||
bind(Collections.singletonMap(name, value));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,264 @@
|
|||
/*
|
||||
* 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.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.catalina.connector.Connector;
|
||||
import org.apache.catalina.core.StandardContext;
|
||||
import org.apache.catalina.core.StandardEngine;
|
||||
import org.apache.catalina.valves.AccessLogValve;
|
||||
import org.apache.catalina.valves.RemoteIpValve;
|
||||
import org.apache.coyote.AbstractProtocol;
|
||||
import org.apache.tomcat.util.net.AbstractEndpoint;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.web.server.tomcat.TomcatServerProperties;
|
||||
import org.springframework.boot.autoconfigure.web.server.tomcat.TomcatServerProperties.Accesslog;
|
||||
import org.springframework.boot.autoconfigure.web.server.tomcat.TomcatServerProperties.UseApr;
|
||||
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.tomcat.TomcatWebServerFactory;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link TomcatServerProperties}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class TomcatServerPropertiesTests {
|
||||
|
||||
private final TomcatServerProperties properties = new TomcatServerProperties();
|
||||
|
||||
@Test
|
||||
void testTomcatBinding() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("server.tomcat.accesslog.conditionIf", "foo");
|
||||
map.put("server.tomcat.accesslog.conditionUnless", "bar");
|
||||
map.put("server.tomcat.accesslog.pattern", "%h %t '%r' %s %b");
|
||||
map.put("server.tomcat.accesslog.prefix", "foo");
|
||||
map.put("server.tomcat.accesslog.suffix", "-bar.log");
|
||||
map.put("server.tomcat.accesslog.encoding", "UTF-8");
|
||||
map.put("server.tomcat.accesslog.locale", "en-AU");
|
||||
map.put("server.tomcat.accesslog.checkExists", "true");
|
||||
map.put("server.tomcat.accesslog.rotate", "false");
|
||||
map.put("server.tomcat.accesslog.rename-on-rotate", "true");
|
||||
map.put("server.tomcat.accesslog.ipv6Canonical", "true");
|
||||
map.put("server.tomcat.accesslog.request-attributes-enabled", "true");
|
||||
map.put("server.tomcat.remoteip.protocol-header", "X-Forwarded-Protocol");
|
||||
map.put("server.tomcat.remoteip.remote-ip-header", "Remote-Ip");
|
||||
map.put("server.tomcat.remoteip.internal-proxies", "10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}");
|
||||
map.put("server.tomcat.remoteip.trusted-proxies", "proxy1|proxy2|proxy3");
|
||||
map.put("server.tomcat.reject-illegal-header", "false");
|
||||
map.put("server.tomcat.background-processor-delay", "10");
|
||||
map.put("server.tomcat.relaxed-path-chars", "|,<");
|
||||
map.put("server.tomcat.relaxed-query-chars", "^ , | ");
|
||||
map.put("server.tomcat.use-relative-redirects", "true");
|
||||
bind(map);
|
||||
Accesslog accesslog = this.properties.getAccesslog();
|
||||
assertThat(accesslog.getConditionIf()).isEqualTo("foo");
|
||||
assertThat(accesslog.getConditionUnless()).isEqualTo("bar");
|
||||
assertThat(accesslog.getPattern()).isEqualTo("%h %t '%r' %s %b");
|
||||
assertThat(accesslog.getPrefix()).isEqualTo("foo");
|
||||
assertThat(accesslog.getSuffix()).isEqualTo("-bar.log");
|
||||
assertThat(accesslog.getEncoding()).isEqualTo("UTF-8");
|
||||
assertThat(accesslog.getLocale()).isEqualTo("en-AU");
|
||||
assertThat(accesslog.isCheckExists()).isTrue();
|
||||
assertThat(accesslog.isRotate()).isFalse();
|
||||
assertThat(accesslog.isRenameOnRotate()).isTrue();
|
||||
assertThat(accesslog.isIpv6Canonical()).isTrue();
|
||||
assertThat(accesslog.isRequestAttributesEnabled()).isTrue();
|
||||
assertThat(this.properties.getRemoteip().getRemoteIpHeader()).isEqualTo("Remote-Ip");
|
||||
assertThat(this.properties.getRemoteip().getProtocolHeader()).isEqualTo("X-Forwarded-Protocol");
|
||||
assertThat(this.properties.getRemoteip().getInternalProxies()).isEqualTo("10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}");
|
||||
assertThat(this.properties.getRemoteip().getTrustedProxies()).isEqualTo("proxy1|proxy2|proxy3");
|
||||
assertThat(this.properties.getBackgroundProcessorDelay()).hasSeconds(10);
|
||||
assertThat(this.properties.getRelaxedPathChars()).containsExactly('|', '<');
|
||||
assertThat(this.properties.getRelaxedQueryChars()).containsExactly('^', '|');
|
||||
assertThat(this.properties.isUseRelativeRedirects()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeTomcatUriEncoding() {
|
||||
bind("server.tomcat.uri-encoding", "US-ASCII");
|
||||
assertThat(this.properties.getUriEncoding()).isEqualTo(StandardCharsets.US_ASCII);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeTomcatMaxThreads() {
|
||||
bind("server.tomcat.threads.max", "10");
|
||||
assertThat(this.properties.getThreads().getMax()).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeTomcatKeepAliveTimeout() {
|
||||
bind("server.tomcat.keep-alive-timeout", "30s");
|
||||
assertThat(this.properties.getKeepAliveTimeout()).hasSeconds(30);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeTomcatKeepAliveTimeoutWithInfinite() {
|
||||
bind("server.tomcat.keep-alive-timeout", "-1");
|
||||
assertThat(this.properties.getKeepAliveTimeout()).hasMillis(-1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeTomcatMaxKeepAliveRequests() {
|
||||
bind("server.tomcat.max-keep-alive-requests", "200");
|
||||
assertThat(this.properties.getMaxKeepAliveRequests()).isEqualTo(200);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeTomcatMaxKeepAliveRequestsWithInfinite() {
|
||||
bind("server.tomcat.max-keep-alive-requests", "-1");
|
||||
assertThat(this.properties.getMaxKeepAliveRequests()).isEqualTo(-1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeTomcatMaxParameterCount() {
|
||||
bind("server.tomcat.max-parameter-count", "100");
|
||||
assertThat(this.properties.getMaxParameterCount()).isEqualTo(100);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeTomcatMinSpareThreads() {
|
||||
bind("server.tomcat.threads.min-spare", "10");
|
||||
assertThat(this.properties.getThreads().getMinSpare()).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatAcceptCountMatchesProtocolDefault() throws Exception {
|
||||
assertThat(this.properties.getAcceptCount()).isEqualTo(getDefaultProtocol().getAcceptCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatProcessorCacheMatchesProtocolDefault() throws Exception {
|
||||
assertThat(this.properties.getProcessorCache()).isEqualTo(getDefaultProtocol().getProcessorCache());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatMaxConnectionsMatchesProtocolDefault() throws Exception {
|
||||
assertThat(this.properties.getMaxConnections()).isEqualTo(getDefaultProtocol().getMaxConnections());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatMaxThreadsMatchesProtocolDefault() throws Exception {
|
||||
assertThat(this.properties.getThreads().getMax()).isEqualTo(getDefaultProtocol().getMaxThreads());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatMinSpareThreadsMatchesProtocolDefault() throws Exception {
|
||||
assertThat(this.properties.getThreads().getMinSpare()).isEqualTo(getDefaultProtocol().getMinSpareThreads());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatMaxHttpPostSizeMatchesConnectorDefault() {
|
||||
assertThat(this.properties.getMaxHttpFormPostSize().toBytes())
|
||||
.isEqualTo(getDefaultConnector().getMaxPostSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatMaxParameterCountMatchesConnectorDefault() {
|
||||
assertThat(this.properties.getMaxParameterCount()).isEqualTo(getDefaultConnector().getMaxParameterCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatBackgroundProcessorDelayMatchesEngineDefault() {
|
||||
assertThat(this.properties.getBackgroundProcessorDelay())
|
||||
.hasSeconds((new StandardEngine().getBackgroundProcessorDelay()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatMaxHttpFormPostSizeMatchesConnectorDefault() {
|
||||
assertThat(this.properties.getMaxHttpFormPostSize().toBytes())
|
||||
.isEqualTo(getDefaultConnector().getMaxPostSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatUriEncodingMatchesConnectorDefault() {
|
||||
assertThat(this.properties.getUriEncoding().name()).isEqualTo(getDefaultConnector().getURIEncoding());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatRedirectContextRootMatchesDefault() {
|
||||
assertThat(this.properties.getRedirectContextRoot())
|
||||
.isEqualTo(new StandardContext().getMapperContextRootRedirectEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatAccessLogRenameOnRotateMatchesDefault() {
|
||||
assertThat(this.properties.getAccesslog().isRenameOnRotate())
|
||||
.isEqualTo(new AccessLogValve().isRenameOnRotate());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatAccessLogRequestAttributesEnabledMatchesDefault() {
|
||||
assertThat(this.properties.getAccesslog().isRequestAttributesEnabled())
|
||||
.isEqualTo(new AccessLogValve().getRequestAttributesEnabled());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatInternalProxiesMatchesDefault() {
|
||||
assertThat(this.properties.getRemoteip().getInternalProxies())
|
||||
.isEqualTo(new RemoteIpValve().getInternalProxies());
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatUseRelativeRedirectsDefaultsToFalse() {
|
||||
assertThat(this.properties.isUseRelativeRedirects()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void tomcatMaxKeepAliveRequestsDefault() throws Exception {
|
||||
AbstractEndpoint<?, ?> endpoint = (AbstractEndpoint<?, ?>) ReflectionTestUtils.getField(getDefaultProtocol(),
|
||||
"endpoint");
|
||||
int defaultMaxKeepAliveRequests = (int) ReflectionTestUtils.getField(endpoint, "maxKeepAliveRequests");
|
||||
assertThat(this.properties.getMaxKeepAliveRequests()).isEqualTo(defaultMaxKeepAliveRequests);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldDefaultAprToNever() {
|
||||
assertThat(this.properties.getUseApr()).isEqualTo(UseApr.NEVER);
|
||||
}
|
||||
|
||||
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.tomcat", Bindable.ofInstance(this.properties));
|
||||
}
|
||||
|
||||
private Connector getDefaultConnector() {
|
||||
return new Connector(TomcatWebServerFactory.DEFAULT_PROTOCOL);
|
||||
}
|
||||
|
||||
private AbstractProtocol<?> getDefaultProtocol() throws Exception {
|
||||
return (AbstractProtocol<?>) Class.forName(TomcatWebServerFactory.DEFAULT_PROTOCOL)
|
||||
.getDeclaredConstructor()
|
||||
.newInstance();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* 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.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import io.undertow.UndertowOptions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.web.server.undertow.UndertowServerProperties;
|
||||
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 static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link UndertowServerProperties}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
class UndertowServerPropertiesTests {
|
||||
|
||||
private final UndertowServerProperties properties = new UndertowServerProperties();
|
||||
|
||||
@Test
|
||||
void testCustomizeUndertowServerOption() {
|
||||
bind("server.undertow.options.server.ALWAYS_SET_KEEP_ALIVE", "true");
|
||||
assertThat(this.properties.getOptions().getServer()).containsEntry("ALWAYS_SET_KEEP_ALIVE", "true");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeUndertowSocketOption() {
|
||||
bind("server.undertow.options.socket.ALWAYS_SET_KEEP_ALIVE", "true");
|
||||
assertThat(this.properties.getOptions().getSocket()).containsEntry("ALWAYS_SET_KEEP_ALIVE", "true");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeUndertowIoThreads() {
|
||||
bind("server.undertow.threads.io", "4");
|
||||
assertThat(this.properties.getThreads().getIo()).isEqualTo(4);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeUndertowWorkerThreads() {
|
||||
bind("server.undertow.threads.worker", "10");
|
||||
assertThat(this.properties.getThreads().getWorker()).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
void undertowMaxHttpPostSizeMatchesDefault() {
|
||||
assertThat(this.properties.getMaxHttpPostSize().toBytes()).isEqualTo(UndertowOptions.DEFAULT_MAX_ENTITY_SIZE);
|
||||
}
|
||||
|
||||
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.undertow", Bindable.ofInstance(this.properties));
|
||||
}
|
||||
|
||||
}
|
|
@ -27,7 +27,6 @@ import org.junit.jupiter.api.Test;
|
|||
import org.junit.jupiter.api.condition.EnabledForJreRange;
|
||||
import org.junit.jupiter.api.condition.JRE;
|
||||
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.web.server.jetty.ConfigurableJettyWebServerFactory;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
@ -45,7 +44,7 @@ class JettyVirtualThreadsWebServerFactoryCustomizerTests {
|
|||
@Test
|
||||
@EnabledForJreRange(min = JRE.JAVA_21)
|
||||
void shouldConfigureVirtualThreads() {
|
||||
ServerProperties properties = new ServerProperties();
|
||||
JettyServerProperties properties = new JettyServerProperties();
|
||||
JettyVirtualThreadsWebServerFactoryCustomizer customizer = new JettyVirtualThreadsWebServerFactoryCustomizer(
|
||||
properties);
|
||||
ConfigurableJettyWebServerFactory factory = mock(ConfigurableJettyWebServerFactory.class);
|
||||
|
|
|
@ -43,11 +43,9 @@ import org.junit.jupiter.api.Test;
|
|||
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties.ForwardHeadersStrategy;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties.Jetty;
|
||||
import org.springframework.boot.context.properties.bind.Bindable;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
|
||||
import org.springframework.boot.testsupport.web.servlet.DirtiesUrlFactories;
|
||||
import org.springframework.boot.web.server.jetty.ConfigurableJettyWebServerFactory;
|
||||
import org.springframework.boot.web.server.jetty.JettyWebServer;
|
||||
import org.springframework.boot.web.server.servlet.jetty.JettyServletWebServerFactory;
|
||||
|
@ -66,21 +64,22 @@ import static org.mockito.Mockito.mock;
|
|||
* @author Phillip Webb
|
||||
* @author HaiTao Zhang
|
||||
*/
|
||||
@DirtiesUrlFactories
|
||||
// @DirtiesUrlFactories
|
||||
class JettyWebServerFactoryCustomizerTests {
|
||||
|
||||
private MockEnvironment environment;
|
||||
private final MockEnvironment environment = new MockEnvironment();
|
||||
|
||||
private ServerProperties serverProperties;
|
||||
private final ServerProperties serverProperties = new ServerProperties();
|
||||
|
||||
private final JettyServerProperties jettyProperties = new JettyServerProperties();
|
||||
|
||||
private JettyWebServerFactoryCustomizer customizer;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.environment = new MockEnvironment();
|
||||
this.serverProperties = new ServerProperties();
|
||||
ConfigurationPropertySources.attach(this.environment);
|
||||
this.customizer = new JettyWebServerFactoryCustomizer(this.environment, this.serverProperties);
|
||||
this.customizer = new JettyWebServerFactoryCustomizer(this.environment, this.serverProperties,
|
||||
this.jettyProperties);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -235,7 +234,7 @@ class JettyWebServerFactoryCustomizerTests {
|
|||
private void assertDefaultThreadPoolSettings(ThreadPool threadPool) {
|
||||
assertThat(threadPool).isInstanceOf(QueuedThreadPool.class);
|
||||
QueuedThreadPool queuedThreadPool = (QueuedThreadPool) threadPool;
|
||||
Jetty defaultProperties = new Jetty();
|
||||
JettyServerProperties defaultProperties = new JettyServerProperties();
|
||||
assertThat(queuedThreadPool.getMinThreads()).isEqualTo(defaultProperties.getThreads().getMin());
|
||||
assertThat(queuedThreadPool.getMaxThreads()).isEqualTo(defaultProperties.getThreads().getMax());
|
||||
assertThat(queuedThreadPool.getIdleTimeout())
|
||||
|
@ -384,8 +383,9 @@ class JettyWebServerFactoryCustomizerTests {
|
|||
|
||||
private void bind(String... inlinedProperties) {
|
||||
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.environment, inlinedProperties);
|
||||
new Binder(ConfigurationPropertySources.get(this.environment)).bind("server",
|
||||
Bindable.ofInstance(this.serverProperties));
|
||||
Binder binder = new Binder(ConfigurationPropertySources.get(this.environment));
|
||||
binder.bind("server", Bindable.ofInstance(this.serverProperties));
|
||||
binder.bind("server.jetty", Bindable.ofInstance(this.jettyProperties));
|
||||
}
|
||||
|
||||
private JettyWebServer customizeAndGetServer() {
|
||||
|
|
|
@ -54,9 +54,11 @@ import static org.mockito.Mockito.times;
|
|||
@ExtendWith(MockitoExtension.class)
|
||||
class NettyReactiveWebServerFactoryCustomizerTests {
|
||||
|
||||
private MockEnvironment environment;
|
||||
private final MockEnvironment environment = new MockEnvironment();
|
||||
|
||||
private ServerProperties serverProperties;
|
||||
private final ServerProperties serverProperties = new ServerProperties();
|
||||
|
||||
private final NettyServerProperties nettyProperties = new NettyServerProperties();
|
||||
|
||||
private NettyReactiveWebServerFactoryCustomizer customizer;
|
||||
|
||||
|
@ -65,10 +67,9 @@ class NettyReactiveWebServerFactoryCustomizerTests {
|
|||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.environment = new MockEnvironment();
|
||||
this.serverProperties = new ServerProperties();
|
||||
ConfigurationPropertySources.attach(this.environment);
|
||||
this.customizer = new NettyReactiveWebServerFactoryCustomizer(this.environment, this.serverProperties);
|
||||
this.customizer = new NettyReactiveWebServerFactoryCustomizer(this.environment, this.serverProperties,
|
||||
this.nettyProperties);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -105,7 +106,7 @@ class NettyReactiveWebServerFactoryCustomizerTests {
|
|||
|
||||
@Test
|
||||
void setConnectionTimeout() {
|
||||
this.serverProperties.getNetty().setConnectionTimeout(Duration.ofSeconds(1));
|
||||
this.nettyProperties.setConnectionTimeout(Duration.ofSeconds(1));
|
||||
NettyReactiveWebServerFactory factory = mock(NettyReactiveWebServerFactory.class);
|
||||
this.customizer.customize(factory);
|
||||
verifyConnectionTimeout(factory, 1000);
|
||||
|
@ -113,7 +114,7 @@ class NettyReactiveWebServerFactoryCustomizerTests {
|
|||
|
||||
@Test
|
||||
void setIdleTimeout() {
|
||||
this.serverProperties.getNetty().setIdleTimeout(Duration.ofSeconds(1));
|
||||
this.nettyProperties.setIdleTimeout(Duration.ofSeconds(1));
|
||||
NettyReactiveWebServerFactory factory = mock(NettyReactiveWebServerFactory.class);
|
||||
this.customizer.customize(factory);
|
||||
verifyIdleTimeout(factory, Duration.ofSeconds(1));
|
||||
|
@ -121,7 +122,7 @@ class NettyReactiveWebServerFactoryCustomizerTests {
|
|||
|
||||
@Test
|
||||
void setMaxKeepAliveRequests() {
|
||||
this.serverProperties.getNetty().setMaxKeepAliveRequests(100);
|
||||
this.nettyProperties.setMaxKeepAliveRequests(100);
|
||||
NettyReactiveWebServerFactory factory = mock(NettyReactiveWebServerFactory.class);
|
||||
this.customizer.customize(factory);
|
||||
verifyMaxKeepAliveRequests(factory, 100);
|
||||
|
@ -139,7 +140,7 @@ class NettyReactiveWebServerFactoryCustomizerTests {
|
|||
|
||||
@Test
|
||||
void configureHttpRequestDecoder() {
|
||||
ServerProperties.Netty nettyProperties = this.serverProperties.getNetty();
|
||||
NettyServerProperties nettyProperties = this.nettyProperties;
|
||||
this.serverProperties.setMaxHttpRequestHeaderSize(DataSize.ofKilobytes(24));
|
||||
nettyProperties.setValidateHeaders(false);
|
||||
nettyProperties.setInitialBufferSize(DataSize.ofBytes(512));
|
||||
|
|
|
@ -164,14 +164,6 @@ class ServletWebServerFactoryCustomizerTests {
|
|||
then(factory).should().setDisplayName("MyBootApp");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCustomizeTomcatMinSpareThreads() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("server.tomcat.threads.min-spare", "10");
|
||||
bindProperties(map);
|
||||
assertThat(this.properties.getTomcat().getThreads().getMinSpare()).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
void sessionStoreDir() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
|
|
|
@ -20,7 +20,7 @@ import org.apache.catalina.Context;
|
|||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.autoconfigure.web.server.tomcat.TomcatServerProperties;
|
||||
import org.springframework.boot.context.properties.bind.Bindable;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
|
||||
|
@ -38,18 +38,17 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
*/
|
||||
class TomcatServletWebServerFactoryCustomizerTests {
|
||||
|
||||
private final TomcatServerProperties tomcatProperties = new TomcatServerProperties();
|
||||
|
||||
private TomcatServletWebServerFactoryCustomizer customizer;
|
||||
|
||||
private MockEnvironment environment;
|
||||
|
||||
private ServerProperties serverProperties;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.environment = new MockEnvironment();
|
||||
this.serverProperties = new ServerProperties();
|
||||
ConfigurationPropertySources.attach(this.environment);
|
||||
this.customizer = new TomcatServletWebServerFactoryCustomizer(this.serverProperties);
|
||||
this.customizer = new TomcatServletWebServerFactoryCustomizer(this.tomcatProperties);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -74,8 +73,7 @@ class TomcatServletWebServerFactoryCustomizerTests {
|
|||
@Test
|
||||
void redirectContextRootCanBeConfigured() {
|
||||
bind("server.tomcat.redirect-context-root=false");
|
||||
ServerProperties.Tomcat tomcat = this.serverProperties.getTomcat();
|
||||
assertThat(tomcat.getRedirectContextRoot()).isFalse();
|
||||
assertThat(this.tomcatProperties.getRedirectContextRoot()).isFalse();
|
||||
TomcatWebServer server = customizeAndGetServer();
|
||||
Context context = (Context) server.getTomcat().getHost().findChildren()[0];
|
||||
assertThat(context.getMapperContextRootRedirectEnabled()).isFalse();
|
||||
|
@ -84,7 +82,7 @@ class TomcatServletWebServerFactoryCustomizerTests {
|
|||
@Test
|
||||
void useRelativeRedirectsCanBeConfigured() {
|
||||
bind("server.tomcat.use-relative-redirects=true");
|
||||
assertThat(this.serverProperties.getTomcat().isUseRelativeRedirects()).isTrue();
|
||||
assertThat(this.tomcatProperties.isUseRelativeRedirects()).isTrue();
|
||||
TomcatWebServer server = customizeAndGetServer();
|
||||
Context context = (Context) server.getTomcat().getHost().findChildren()[0];
|
||||
assertThat(context.getUseRelativeRedirects()).isTrue();
|
||||
|
@ -92,8 +90,8 @@ class TomcatServletWebServerFactoryCustomizerTests {
|
|||
|
||||
private void bind(String... inlinedProperties) {
|
||||
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.environment, inlinedProperties);
|
||||
new Binder(ConfigurationPropertySources.get(this.environment)).bind("server",
|
||||
Bindable.ofInstance(this.serverProperties));
|
||||
new Binder(ConfigurationPropertySources.get(this.environment)).bind("server.tomcat",
|
||||
Bindable.ofInstance(this.tomcatProperties));
|
||||
}
|
||||
|
||||
private TomcatWebServer customizeAndGetServer() {
|
||||
|
|
|
@ -18,7 +18,7 @@ package org.springframework.boot.autoconfigure.web.server.servlet.undertow;
|
|||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.autoconfigure.web.server.undertow.UndertowServerProperties;
|
||||
import org.springframework.boot.web.server.servlet.undertow.UndertowServletWebServerFactory;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
@ -34,9 +34,9 @@ class UndertowServletWebServerFactoryCustomizerTests {
|
|||
void eagerFilterInitCanBeDisabled() {
|
||||
UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory(0);
|
||||
assertThat(factory.isEagerFilterInit()).isTrue();
|
||||
ServerProperties serverProperties = new ServerProperties();
|
||||
serverProperties.getUndertow().setEagerFilterInit(false);
|
||||
new UndertowServletWebServerFactoryCustomizer(serverProperties).customize(factory);
|
||||
UndertowServerProperties undertowProperties = new UndertowServerProperties();
|
||||
undertowProperties.setEagerFilterInit(false);
|
||||
new UndertowServletWebServerFactoryCustomizer(undertowProperties).customize(factory);
|
||||
assertThat(factory.isEagerFilterInit()).isFalse();
|
||||
}
|
||||
|
||||
|
@ -44,9 +44,9 @@ class UndertowServletWebServerFactoryCustomizerTests {
|
|||
void preservePathOnForwardCanBeEnabled() {
|
||||
UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory(0);
|
||||
assertThat(factory.isPreservePathOnForward()).isFalse();
|
||||
ServerProperties serverProperties = new ServerProperties();
|
||||
serverProperties.getUndertow().setPreservePathOnForward(true);
|
||||
new UndertowServletWebServerFactoryCustomizer(serverProperties).customize(factory);
|
||||
UndertowServerProperties undertowProperties = new UndertowServerProperties();
|
||||
undertowProperties.setPreservePathOnForward(true);
|
||||
new UndertowServletWebServerFactoryCustomizer(undertowProperties).customize(factory);
|
||||
assertThat(factory.isPreservePathOnForward()).isTrue();
|
||||
}
|
||||
|
||||
|
|
|
@ -62,18 +62,19 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
*/
|
||||
class TomcatWebServerFactoryCustomizerTests {
|
||||
|
||||
private MockEnvironment environment;
|
||||
private final MockEnvironment environment = new MockEnvironment();
|
||||
|
||||
private ServerProperties serverProperties;
|
||||
private final ServerProperties serverProperties = new ServerProperties();
|
||||
|
||||
private final TomcatServerProperties tomcatProperties = new TomcatServerProperties();
|
||||
|
||||
private TomcatWebServerFactoryCustomizer customizer;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.environment = new MockEnvironment();
|
||||
this.serverProperties = new ServerProperties();
|
||||
ConfigurationPropertySources.attach(this.environment);
|
||||
this.customizer = new TomcatWebServerFactoryCustomizer(this.environment, this.serverProperties);
|
||||
this.customizer = new TomcatWebServerFactoryCustomizer(this.environment, this.serverProperties,
|
||||
this.tomcatProperties);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -81,7 +82,7 @@ class TomcatWebServerFactoryCustomizerTests {
|
|||
customizeAndRunServer((server) -> assertThat(
|
||||
((AbstractHttp11Protocol<?>) server.getTomcat().getConnector().getProtocolHandler())
|
||||
.getMaxSwallowSize())
|
||||
.isEqualTo(this.serverProperties.getTomcat().getMaxSwallowSize().toBytes()));
|
||||
.isEqualTo(this.tomcatProperties.getMaxSwallowSize().toBytes()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -422,7 +423,7 @@ class TomcatWebServerFactoryCustomizerTests {
|
|||
@Test
|
||||
void testCustomizeMinSpareThreads() {
|
||||
bind("server.tomcat.threads.min-spare=10");
|
||||
assertThat(this.serverProperties.getTomcat().getThreads().getMinSpare()).isEqualTo(10);
|
||||
assertThat(this.tomcatProperties.getThreads().getMinSpare()).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -482,7 +483,7 @@ class TomcatWebServerFactoryCustomizerTests {
|
|||
bind("server.tomcat.accesslog.enabled=true");
|
||||
TomcatServletWebServerFactory factory = customizeAndGetFactory();
|
||||
assertThat(((AccessLogValve) factory.getEngineValves().iterator().next()).getMaxDays())
|
||||
.isEqualTo(this.serverProperties.getTomcat().getAccesslog().getMaxDays());
|
||||
.isEqualTo(this.tomcatProperties.getAccesslog().getMaxDays());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -589,8 +590,9 @@ class TomcatWebServerFactoryCustomizerTests {
|
|||
|
||||
private void bind(String... inlinedProperties) {
|
||||
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.environment, inlinedProperties);
|
||||
new Binder(ConfigurationPropertySources.get(this.environment)).bind("server",
|
||||
Bindable.ofInstance(this.serverProperties));
|
||||
Binder binder = new Binder(ConfigurationPropertySources.get(this.environment));
|
||||
binder.bind("server", Bindable.ofInstance(this.serverProperties));
|
||||
binder.bind("server.tomcat", Bindable.ofInstance(this.tomcatProperties));
|
||||
}
|
||||
|
||||
private void customizeAndRunServer(Consumer<TomcatWebServer> consumer) {
|
||||
|
|
|
@ -56,18 +56,19 @@ import static org.mockito.Mockito.mock;
|
|||
*/
|
||||
class UndertowWebServerFactoryCustomizerTests {
|
||||
|
||||
private MockEnvironment environment;
|
||||
private final MockEnvironment environment = new MockEnvironment();
|
||||
|
||||
private ServerProperties serverProperties;
|
||||
private final ServerProperties serverProperties = new ServerProperties();
|
||||
|
||||
private final UndertowServerProperties undertowProperties = new UndertowServerProperties();
|
||||
|
||||
private UndertowWebServerFactoryCustomizer customizer;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.environment = new MockEnvironment();
|
||||
this.serverProperties = new ServerProperties();
|
||||
ConfigurationPropertySources.attach(this.environment);
|
||||
this.customizer = new UndertowWebServerFactoryCustomizer(this.environment, this.serverProperties);
|
||||
this.customizer = new UndertowWebServerFactoryCustomizer(this.environment, this.serverProperties,
|
||||
this.undertowProperties);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -266,8 +267,9 @@ class UndertowWebServerFactoryCustomizerTests {
|
|||
|
||||
private void bind(String... inlinedProperties) {
|
||||
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.environment, inlinedProperties);
|
||||
new Binder(ConfigurationPropertySources.get(this.environment)).bind("server",
|
||||
Bindable.ofInstance(this.serverProperties));
|
||||
Binder binder = new Binder(ConfigurationPropertySources.get(this.environment));
|
||||
binder.bind("server", Bindable.ofInstance(this.serverProperties));
|
||||
binder.bind("server.undertow", Bindable.ofInstance(this.undertowProperties));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue