Apply server.tomcat.* config to reactive servers
This commit applies most `server.tomcat.*` configuration properties to Tomcat when set up as a reactive web server. Some Servlet-specific properties are not applied: * server.tomcat.additional-tld-skip-patterns * server.tomcat.redirect-context-root * server.tomcat.use-relative-redirects Fixes gh-11334pull/11497/merge
parent
4b59d5f517
commit
d264af8142
@ -0,0 +1,230 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.web.embedded.tomcat;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.apache.catalina.Lifecycle;
|
||||
import org.apache.catalina.valves.AccessLogValve;
|
||||
import org.apache.catalina.valves.RemoteIpValve;
|
||||
import org.apache.coyote.AbstractProtocol;
|
||||
import org.apache.coyote.ProtocolHandler;
|
||||
import org.apache.coyote.http11.AbstractHttp11Protocol;
|
||||
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.cloud.CloudPlatform;
|
||||
import org.springframework.boot.web.embedded.tomcat.ConfigurableTomcatWebServerFactory;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Customization for Tomcat-specific features common
|
||||
* for both Servlet and Reactive servers.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public final class TomcatCustomizer {
|
||||
|
||||
private TomcatCustomizer() {
|
||||
}
|
||||
|
||||
public static void customizeTomcat(ServerProperties serverProperties,
|
||||
Environment environment, ConfigurableTomcatWebServerFactory factory) {
|
||||
ServerProperties.Tomcat tomcatProperties = serverProperties.getTomcat();
|
||||
if (tomcatProperties.getBasedir() != null) {
|
||||
factory.setBaseDirectory(tomcatProperties.getBasedir());
|
||||
}
|
||||
if (tomcatProperties.getBackgroundProcessorDelay() != null) {
|
||||
factory.setBackgroundProcessorDelay((int) tomcatProperties
|
||||
.getBackgroundProcessorDelay().getSeconds());
|
||||
}
|
||||
customizeRemoteIpValve(serverProperties, environment, factory);
|
||||
if (tomcatProperties.getMaxThreads() > 0) {
|
||||
customizeMaxThreads(factory, tomcatProperties.getMaxThreads());
|
||||
}
|
||||
if (tomcatProperties.getMinSpareThreads() > 0) {
|
||||
customizeMinThreads(factory, tomcatProperties.getMinSpareThreads());
|
||||
}
|
||||
int maxHttpHeaderSize = (serverProperties.getMaxHttpHeaderSize() > 0
|
||||
? serverProperties.getMaxHttpHeaderSize()
|
||||
: tomcatProperties.getMaxHttpHeaderSize());
|
||||
if (maxHttpHeaderSize > 0) {
|
||||
customizeMaxHttpHeaderSize(factory, maxHttpHeaderSize);
|
||||
}
|
||||
if (tomcatProperties.getMaxHttpPostSize() != 0) {
|
||||
customizeMaxHttpPostSize(factory, tomcatProperties.getMaxHttpPostSize());
|
||||
}
|
||||
if (tomcatProperties.getAccesslog().isEnabled()) {
|
||||
customizeAccessLog(tomcatProperties, factory);
|
||||
}
|
||||
if (tomcatProperties.getUriEncoding() != null) {
|
||||
factory.setUriEncoding(tomcatProperties.getUriEncoding());
|
||||
}
|
||||
if (serverProperties.getConnectionTimeout() != null) {
|
||||
customizeConnectionTimeout(factory,
|
||||
serverProperties.getConnectionTimeout());
|
||||
}
|
||||
if (tomcatProperties.getMaxConnections() > 0) {
|
||||
customizeMaxConnections(factory, tomcatProperties.getMaxConnections());
|
||||
}
|
||||
if (tomcatProperties.getAcceptCount() > 0) {
|
||||
customizeAcceptCount(factory, tomcatProperties.getAcceptCount());
|
||||
}
|
||||
customizeStaticResources(serverProperties.getTomcat().getResource(), factory);
|
||||
}
|
||||
|
||||
private static void customizeAcceptCount(ConfigurableTomcatWebServerFactory factory,
|
||||
int acceptCount) {
|
||||
factory.addConnectorCustomizers((connector) -> {
|
||||
ProtocolHandler handler = connector.getProtocolHandler();
|
||||
if (handler instanceof AbstractProtocol) {
|
||||
AbstractProtocol<?> protocol = (AbstractProtocol<?>) handler;
|
||||
protocol.setAcceptCount(acceptCount);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void customizeMaxConnections(ConfigurableTomcatWebServerFactory factory,
|
||||
int maxConnections) {
|
||||
factory.addConnectorCustomizers((connector) -> {
|
||||
ProtocolHandler handler = connector.getProtocolHandler();
|
||||
if (handler instanceof AbstractProtocol) {
|
||||
AbstractProtocol<?> protocol = (AbstractProtocol<?>) handler;
|
||||
protocol.setMaxConnections(maxConnections);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void customizeConnectionTimeout(
|
||||
ConfigurableTomcatWebServerFactory factory, Duration connectionTimeout) {
|
||||
factory.addConnectorCustomizers((connector) -> {
|
||||
ProtocolHandler handler = connector.getProtocolHandler();
|
||||
if (handler instanceof AbstractProtocol) {
|
||||
AbstractProtocol<?> protocol = (AbstractProtocol<?>) handler;
|
||||
protocol.setConnectionTimeout((int) connectionTimeout.toMillis());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void customizeRemoteIpValve(ServerProperties properties,
|
||||
Environment environment, ConfigurableTomcatWebServerFactory factory) {
|
||||
String protocolHeader = properties.getTomcat().getProtocolHeader();
|
||||
String remoteIpHeader = properties.getTomcat().getRemoteIpHeader();
|
||||
// For back compatibility the valve is also enabled if protocol-header is set
|
||||
if (StringUtils.hasText(protocolHeader) || StringUtils.hasText(remoteIpHeader)
|
||||
|| getOrDeduceUseForwardHeaders(properties, environment)) {
|
||||
RemoteIpValve valve = new RemoteIpValve();
|
||||
valve.setProtocolHeader(StringUtils.hasLength(protocolHeader)
|
||||
? protocolHeader : "X-Forwarded-Proto");
|
||||
if (StringUtils.hasLength(remoteIpHeader)) {
|
||||
valve.setRemoteIpHeader(remoteIpHeader);
|
||||
}
|
||||
// The internal proxies default to a white list of "safe" internal IP
|
||||
// addresses
|
||||
valve.setInternalProxies(properties.getTomcat().getInternalProxies());
|
||||
valve.setPortHeader(properties.getTomcat().getPortHeader());
|
||||
valve.setProtocolHeaderHttpsValue(
|
||||
properties.getTomcat().getProtocolHeaderHttpsValue());
|
||||
// ... so it's safe to add this valve by default.
|
||||
factory.addEngineValves(valve);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean getOrDeduceUseForwardHeaders(ServerProperties serverProperties,
|
||||
Environment environment) {
|
||||
if (serverProperties.isUseForwardHeaders() != null) {
|
||||
return serverProperties.isUseForwardHeaders();
|
||||
}
|
||||
CloudPlatform platform = CloudPlatform.getActive(environment);
|
||||
return platform != null && platform.isUsingForwardHeaders();
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private static void customizeMaxThreads(ConfigurableTomcatWebServerFactory factory,
|
||||
int maxThreads) {
|
||||
factory.addConnectorCustomizers((connector) -> {
|
||||
ProtocolHandler handler = connector.getProtocolHandler();
|
||||
if (handler instanceof AbstractProtocol) {
|
||||
AbstractProtocol protocol = (AbstractProtocol) handler;
|
||||
protocol.setMaxThreads(maxThreads);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private static void customizeMinThreads(ConfigurableTomcatWebServerFactory factory,
|
||||
int minSpareThreads) {
|
||||
factory.addConnectorCustomizers((connector) -> {
|
||||
ProtocolHandler handler = connector.getProtocolHandler();
|
||||
if (handler instanceof AbstractProtocol) {
|
||||
AbstractProtocol protocol = (AbstractProtocol) handler;
|
||||
protocol.setMinSpareThreads(minSpareThreads);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private static void customizeMaxHttpHeaderSize(
|
||||
ConfigurableTomcatWebServerFactory factory, int maxHttpHeaderSize) {
|
||||
factory.addConnectorCustomizers((connector) -> {
|
||||
ProtocolHandler handler = connector.getProtocolHandler();
|
||||
if (handler instanceof AbstractHttp11Protocol) {
|
||||
AbstractHttp11Protocol protocol = (AbstractHttp11Protocol) handler;
|
||||
protocol.setMaxHttpHeaderSize(maxHttpHeaderSize);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void customizeMaxHttpPostSize(
|
||||
ConfigurableTomcatWebServerFactory factory, int maxHttpPostSize) {
|
||||
factory.addConnectorCustomizers(
|
||||
(connector) -> connector.setMaxPostSize(maxHttpPostSize));
|
||||
}
|
||||
|
||||
private static void customizeAccessLog(ServerProperties.Tomcat tomcatProperties,
|
||||
ConfigurableTomcatWebServerFactory factory) {
|
||||
|
||||
AccessLogValve valve = new AccessLogValve();
|
||||
valve.setPattern(tomcatProperties.getAccesslog().getPattern());
|
||||
valve.setDirectory(tomcatProperties.getAccesslog().getDirectory());
|
||||
valve.setPrefix(tomcatProperties.getAccesslog().getPrefix());
|
||||
valve.setSuffix(tomcatProperties.getAccesslog().getSuffix());
|
||||
valve.setRenameOnRotate(tomcatProperties.getAccesslog().isRenameOnRotate());
|
||||
valve.setFileDateFormat(tomcatProperties.getAccesslog().getFileDateFormat());
|
||||
valve.setRequestAttributesEnabled(
|
||||
tomcatProperties.getAccesslog().isRequestAttributesEnabled());
|
||||
valve.setRotatable(tomcatProperties.getAccesslog().isRotate());
|
||||
valve.setBuffered(tomcatProperties.getAccesslog().isBuffered());
|
||||
factory.addEngineValves(valve);
|
||||
}
|
||||
|
||||
private static void customizeStaticResources(ServerProperties.Tomcat.Resource resource,
|
||||
ConfigurableTomcatWebServerFactory factory) {
|
||||
if (resource.getCacheTtl() == null) {
|
||||
return;
|
||||
}
|
||||
factory.addContextCustomizers((context) -> {
|
||||
context.addLifecycleListener((event) -> {
|
||||
if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
|
||||
long ttl = resource.getCacheTtl().toMillis();
|
||||
context.getResources().setCacheTtl(ttl);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Configuration for embedded reactive and servlet Tomcat web servers.
|
||||
*
|
||||
* @see org.springframework.boot.web.embedded.tomcat.ConfigurableTomcatWebServerFactory
|
||||
*/
|
||||
package org.springframework.boot.autoconfigure.web.embedded.tomcat;
|
35
spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/DefaultReactiveWebServerCustomizer.java → spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/DefaultReactiveWebServerFactoryCustomizer.java
35
spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/DefaultReactiveWebServerCustomizer.java → spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/DefaultReactiveWebServerFactoryCustomizer.java
@ -1,65 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.web.reactive;
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.web.reactive.server.ConfigurableReactiveWebServerFactory;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Tests for {@link DefaultReactiveWebServerCustomizer}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
public class DefaultReactiveWebServerCustomizerTests {
|
||||
|
||||
private final ServerProperties properties = new ServerProperties();
|
||||
|
||||
private DefaultReactiveWebServerCustomizer customizer;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.customizer = new DefaultReactiveWebServerCustomizer(this.properties);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomizeServerPort() {
|
||||
ConfigurableReactiveWebServerFactory factory = mock(
|
||||
ConfigurableReactiveWebServerFactory.class);
|
||||
this.properties.setPort(9000);
|
||||
this.customizer.customize(factory);
|
||||
verify(factory).setPort(9000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomizeServerAddress() {
|
||||
ConfigurableReactiveWebServerFactory factory = mock(
|
||||
ConfigurableReactiveWebServerFactory.class);
|
||||
InetAddress address = mock(InetAddress.class);
|
||||
this.properties.setAddress(address);
|
||||
this.customizer.customize(factory);
|
||||
verify(factory).setAddress(address);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,348 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.web.reactive;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.catalina.Context;
|
||||
import org.apache.catalina.Valve;
|
||||
import org.apache.catalina.startup.Tomcat;
|
||||
import org.apache.catalina.valves.AccessLogValve;
|
||||
import org.apache.catalina.valves.RemoteIpValve;
|
||||
import org.apache.coyote.AbstractProtocol;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.boot.context.properties.bind.Bindable;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
|
||||
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
|
||||
import org.springframework.boot.web.embedded.tomcat.TomcatReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.embedded.tomcat.TomcatWebServer;
|
||||
import org.springframework.boot.web.reactive.server.ConfigurableReactiveWebServerFactory;
|
||||
import org.springframework.http.server.reactive.HttpHandler;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* Tests for {@link DefaultReactiveWebServerFactoryCustomizer}.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
*/
|
||||
public class DefaultReactiveWebServerFactoryCustomizerTests {
|
||||
|
||||
private final ServerProperties properties = new ServerProperties();
|
||||
|
||||
private DefaultReactiveWebServerFactoryCustomizer customizer;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.customizer = new DefaultReactiveWebServerFactoryCustomizer(this.properties);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomizeServerPort() {
|
||||
ConfigurableReactiveWebServerFactory factory = mock(
|
||||
ConfigurableReactiveWebServerFactory.class);
|
||||
this.properties.setPort(9000);
|
||||
this.customizer.customize(factory);
|
||||
verify(factory).setPort(9000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomizeServerAddress() {
|
||||
ConfigurableReactiveWebServerFactory factory = mock(
|
||||
ConfigurableReactiveWebServerFactory.class);
|
||||
InetAddress address = mock(InetAddress.class);
|
||||
this.properties.setAddress(address);
|
||||
this.customizer.customize(factory);
|
||||
verify(factory).setAddress(address);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tomcatAccessLogIsDisabledByDefault() {
|
||||
TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory();
|
||||
this.customizer.customize(factory);
|
||||
assertThat(factory.getEngineValves()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tomcatAccessLogCanBeEnabled() {
|
||||
TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory();
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("server.tomcat.accesslog.enabled", "true");
|
||||
bindProperties(map);
|
||||
this.customizer.customize(factory);
|
||||
assertThat(factory.getEngineValves()).hasSize(1);
|
||||
assertThat(factory.getEngineValves()).first().isInstanceOf(AccessLogValve.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tomcatAccessLogFileDateFormatByDefault() {
|
||||
TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory();
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("server.tomcat.accesslog.enabled", "true");
|
||||
bindProperties(map);
|
||||
this.customizer.customize(factory);
|
||||
assertThat(((AccessLogValve) factory.getEngineValves().iterator().next())
|
||||
.getFileDateFormat()).isEqualTo(".yyyy-MM-dd");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tomcatAccessLogFileDateFormatCanBeRedefined() {
|
||||
TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory();
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("server.tomcat.accesslog.enabled", "true");
|
||||
map.put("server.tomcat.accesslog.file-date-format", "yyyy-MM-dd.HH");
|
||||
bindProperties(map);
|
||||
this.customizer.customize(factory);
|
||||
assertThat(((AccessLogValve) factory.getEngineValves().iterator().next())
|
||||
.getFileDateFormat()).isEqualTo("yyyy-MM-dd.HH");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tomcatAccessLogIsBufferedByDefault() {
|
||||
TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory();
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("server.tomcat.accesslog.enabled", "true");
|
||||
bindProperties(map);
|
||||
this.customizer.customize(factory);
|
||||
assertThat(((AccessLogValve) factory.getEngineValves().iterator().next())
|
||||
.isBuffered()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tomcatAccessLogBufferingCanBeDisabled() {
|
||||
TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory();
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("server.tomcat.accesslog.enabled", "true");
|
||||
map.put("server.tomcat.accesslog.buffered", "false");
|
||||
bindProperties(map);
|
||||
this.customizer.customize(factory);
|
||||
assertThat(((AccessLogValve) factory.getEngineValves().iterator().next())
|
||||
.isBuffered()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void disableTomcatRemoteIpValve() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("server.tomcat.remote-ip-header", "");
|
||||
map.put("server.tomcat.protocol-header", "");
|
||||
bindProperties(map);
|
||||
TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory();
|
||||
this.customizer.customize(factory);
|
||||
assertThat(factory.getEngineValves()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultTomcatBackgroundProcessorDelay() {
|
||||
TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory();
|
||||
this.customizer.customize(factory);
|
||||
TomcatWebServer webServer = (TomcatWebServer) factory.getWebServer(mock(HttpHandler.class));
|
||||
assertThat(webServer.getTomcat().getEngine().getBackgroundProcessorDelay())
|
||||
.isEqualTo(30);
|
||||
webServer.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customTomcatBackgroundProcessorDelay() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("server.tomcat.background-processor-delay", "5");
|
||||
bindProperties(map);
|
||||
TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory();
|
||||
this.customizer.customize(factory);
|
||||
TomcatWebServer webServer = (TomcatWebServer) factory.getWebServer(mock(HttpHandler.class));
|
||||
assertThat(webServer.getTomcat().getEngine().getBackgroundProcessorDelay())
|
||||
.isEqualTo(5);
|
||||
webServer.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultTomcatRemoteIpValve() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
// Since 1.1.7 you need to specify at least the protocol
|
||||
map.put("server.tomcat.protocol-header", "X-Forwarded-Proto");
|
||||
map.put("server.tomcat.remote-ip-header", "X-Forwarded-For");
|
||||
bindProperties(map);
|
||||
testRemoteIpValveConfigured();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setUseForwardHeadersTomcat() {
|
||||
// Since 1.3.0 no need to explicitly set header names if use-forward-header=true
|
||||
this.properties.setUseForwardHeaders(true);
|
||||
testRemoteIpValveConfigured();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deduceUseForwardHeadersTomcat() {
|
||||
this.customizer.setEnvironment(new MockEnvironment().withProperty("DYNO", "-"));
|
||||
testRemoteIpValveConfigured();
|
||||
}
|
||||
|
||||
private void testRemoteIpValveConfigured() {
|
||||
TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory();
|
||||
this.customizer.customize(factory);
|
||||
assertThat(factory.getEngineValves()).hasSize(1);
|
||||
Valve valve = factory.getEngineValves().iterator().next();
|
||||
assertThat(valve).isInstanceOf(RemoteIpValve.class);
|
||||
RemoteIpValve remoteIpValve = (RemoteIpValve) valve;
|
||||
assertThat(remoteIpValve.getProtocolHeader()).isEqualTo("X-Forwarded-Proto");
|
||||
assertThat(remoteIpValve.getProtocolHeaderHttpsValue()).isEqualTo("https");
|
||||
assertThat(remoteIpValve.getRemoteIpHeader()).isEqualTo("X-Forwarded-For");
|
||||
String expectedInternalProxies = "10\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" // 10/8
|
||||
+ "192\\.168\\.\\d{1,3}\\.\\d{1,3}|" // 192.168/16
|
||||
+ "169\\.254\\.\\d{1,3}\\.\\d{1,3}|" // 169.254/16
|
||||
+ "127\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}|" // 127/8
|
||||
+ "172\\.1[6-9]{1}\\.\\d{1,3}\\.\\d{1,3}|" // 172.16/12
|
||||
+ "172\\.2[0-9]{1}\\.\\d{1,3}\\.\\d{1,3}|"
|
||||
+ "172\\.3[0-1]{1}\\.\\d{1,3}\\.\\d{1,3}";
|
||||
assertThat(remoteIpValve.getInternalProxies()).isEqualTo(expectedInternalProxies);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customTomcatRemoteIpValve() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("server.tomcat.remote-ip-header", "x-my-remote-ip-header");
|
||||
map.put("server.tomcat.protocol-header", "x-my-protocol-header");
|
||||
map.put("server.tomcat.internal-proxies", "192.168.0.1");
|
||||
map.put("server.tomcat.port-header", "x-my-forward-port");
|
||||
map.put("server.tomcat.protocol-header-https-value", "On");
|
||||
bindProperties(map);
|
||||
TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory();
|
||||
this.customizer.customize(factory);
|
||||
assertThat(factory.getEngineValves()).hasSize(1);
|
||||
Valve valve = factory.getEngineValves().iterator().next();
|
||||
assertThat(valve).isInstanceOf(RemoteIpValve.class);
|
||||
RemoteIpValve remoteIpValve = (RemoteIpValve) valve;
|
||||
assertThat(remoteIpValve.getProtocolHeader()).isEqualTo("x-my-protocol-header");
|
||||
assertThat(remoteIpValve.getProtocolHeaderHttpsValue()).isEqualTo("On");
|
||||
assertThat(remoteIpValve.getRemoteIpHeader()).isEqualTo("x-my-remote-ip-header");
|
||||
assertThat(remoteIpValve.getPortHeader()).isEqualTo("x-my-forward-port");
|
||||
assertThat(remoteIpValve.getInternalProxies()).isEqualTo("192.168.0.1");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customTomcatAcceptCount() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("server.tomcat.accept-count", "10");
|
||||
bindProperties(map);
|
||||
TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory(0);
|
||||
this.customizer.customize(factory);
|
||||
TomcatWebServer server = (TomcatWebServer) factory.getWebServer(mock(HttpHandler.class));
|
||||
server.start();
|
||||
try {
|
||||
assertThat(((AbstractProtocol<?>) server.getTomcat().getConnector()
|
||||
.getProtocolHandler()).getAcceptCount()).isEqualTo(10);
|
||||
}
|
||||
finally {
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customTomcatMaxConnections() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("server.tomcat.max-connections", "5");
|
||||
bindProperties(map);
|
||||
TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory(0);
|
||||
this.customizer.customize(factory);
|
||||
TomcatWebServer server = (TomcatWebServer) factory.getWebServer(mock(HttpHandler.class));
|
||||
server.start();
|
||||
try {
|
||||
assertThat(((AbstractProtocol<?>) server.getTomcat().getConnector()
|
||||
.getProtocolHandler()).getMaxConnections()).isEqualTo(5);
|
||||
}
|
||||
finally {
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customTomcatMaxHttpPostSize() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("server.tomcat.max-http-post-size", "10000");
|
||||
bindProperties(map);
|
||||
TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory(0);
|
||||
this.customizer.customize(factory);
|
||||
TomcatWebServer server = (TomcatWebServer) factory.getWebServer(mock(HttpHandler.class));
|
||||
server.start();
|
||||
try {
|
||||
assertThat(server.getTomcat().getConnector().getMaxPostSize())
|
||||
.isEqualTo(10000);
|
||||
}
|
||||
finally {
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customTomcatDisableMaxHttpPostSize() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("server.tomcat.max-http-post-size", "-1");
|
||||
bindProperties(map);
|
||||
TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory(0);
|
||||
this.customizer.customize(factory);
|
||||
TomcatWebServer server = (TomcatWebServer) factory.getWebServer(mock(HttpHandler.class));
|
||||
server.start();
|
||||
try {
|
||||
assertThat(server.getTomcat().getConnector().getMaxPostSize()).isEqualTo(-1);
|
||||
}
|
||||
finally {
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomizeTomcatMinSpareThreads() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("server.tomcat.min-spare-threads", "10");
|
||||
bindProperties(map);
|
||||
assertThat(this.properties.getTomcat().getMinSpareThreads()).isEqualTo(10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customTomcatStaticResourceCacheTtl() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("server.tomcat.resource.cache-ttl", "10000");
|
||||
bindProperties(map);
|
||||
TomcatReactiveWebServerFactory factory = new TomcatReactiveWebServerFactory(0);
|
||||
this.customizer.customize(factory);
|
||||
TomcatWebServer server = (TomcatWebServer) factory.getWebServer(mock(HttpHandler.class));
|
||||
server.start();
|
||||
try {
|
||||
Tomcat tomcat = server.getTomcat();
|
||||
Context context = (Context) tomcat.getHost().findChildren()[0];
|
||||
assertThat(context.getResources().getCacheTtl()).isEqualTo(10000L);
|
||||
}
|
||||
finally {
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
|
||||
private void bindProperties(Map<String, String> map) {
|
||||
ConfigurationPropertySource source = new MapConfigurationPropertySource(map);
|
||||
new Binder(source).bind("server", Bindable.ofInstance(this.properties));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.web.embedded.tomcat;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import org.apache.catalina.Context;
|
||||
import org.apache.catalina.Engine;
|
||||
import org.apache.catalina.Valve;
|
||||
import org.apache.catalina.connector.Connector;
|
||||
|
||||
/**
|
||||
* Web Server Factory configuration for Tomcat-specific features.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 2.0.0
|
||||
* @see TomcatServletWebServerFactory
|
||||
* @see TomcatReactiveWebServerFactory
|
||||
*/
|
||||
public interface ConfigurableTomcatWebServerFactory {
|
||||
|
||||
/**
|
||||
* Set the Tomcat base directory. If not specified a temporary directory will be used.
|
||||
* @param baseDirectory the tomcat base directory
|
||||
*/
|
||||
void setBaseDirectory(File baseDirectory);
|
||||
|
||||
/**
|
||||
* Sets the background processor delay in seconds.
|
||||
* @param delay the delay in seconds
|
||||
*/
|
||||
void setBackgroundProcessorDelay(int delay);
|
||||
|
||||
/**
|
||||
* Add {@link Valve}s that should be applied to the Tomcat {@link Engine}.
|
||||
* @param engineValves the valves to add
|
||||
*/
|
||||
void addEngineValves(Valve... engineValves);
|
||||
|
||||
/**
|
||||
* Add {@link TomcatConnectorCustomizer}s that should be added to the Tomcat
|
||||
* {@link Connector}.
|
||||
* @param tomcatConnectorCustomizers the customizers to add
|
||||
*/
|
||||
void addConnectorCustomizers(TomcatConnectorCustomizer... tomcatConnectorCustomizers);
|
||||
|
||||
/**
|
||||
* Add {@link TomcatContextCustomizer}s that should be added to the Tomcat
|
||||
* {@link Context}.
|
||||
* @param tomcatContextCustomizers the customizers to add
|
||||
*/
|
||||
void addContextCustomizers(TomcatContextCustomizer... tomcatContextCustomizers);
|
||||
|
||||
/**
|
||||
* Set the character encoding to use for URL decoding. If not specified 'UTF-8' will
|
||||
* be used.
|
||||
* @param uriEncoding the uri encoding to set
|
||||
*/
|
||||
void setUriEncoding(Charset uriEncoding);
|
||||
|
||||
}
|
Loading…
Reference in New Issue