Merge pull request #17356 from htztomic
* pr/17356: Polish "Allow Undertow's options to be configured via the environment Allow Undertow's options to be configured via the environment Closes gh-17356
This commit is contained in:
commit
c19bed15d2
|
|
@ -24,6 +24,7 @@ import java.time.Duration;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
@ -1119,6 +1120,8 @@ public class ServerProperties {
|
||||||
|
|
||||||
private final Accesslog accesslog = new Accesslog();
|
private final Accesslog accesslog = new Accesslog();
|
||||||
|
|
||||||
|
private final Options options = new Options();
|
||||||
|
|
||||||
public DataSize getMaxHttpPostSize() {
|
public DataSize getMaxHttpPostSize() {
|
||||||
return this.maxHttpPostSize;
|
return this.maxHttpPostSize;
|
||||||
}
|
}
|
||||||
|
|
@ -1227,6 +1230,10 @@ public class ServerProperties {
|
||||||
return this.accesslog;
|
return this.accesslog;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Options getOptions() {
|
||||||
|
return this.options;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Undertow access log properties.
|
* Undertow access log properties.
|
||||||
*/
|
*/
|
||||||
|
|
@ -1312,6 +1319,22 @@ public class ServerProperties {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class Options {
|
||||||
|
|
||||||
|
private Map<String, String> socket = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
private Map<String, String> server = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
public Map<String, String> getServer() {
|
||||||
|
return this.server;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getSocket() {
|
||||||
|
return this.socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package org.springframework.boot.autoconfigure.web.embedded;
|
package org.springframework.boot.autoconfigure.web.embedded;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
import io.undertow.UndertowOptions;
|
import io.undertow.UndertowOptions;
|
||||||
import org.xnio.Option;
|
import org.xnio.Option;
|
||||||
|
|
||||||
|
|
@ -61,6 +63,7 @@ public class UndertowWebServerFactoryCustomizer
|
||||||
public void customize(ConfigurableUndertowWebServerFactory factory) {
|
public void customize(ConfigurableUndertowWebServerFactory factory) {
|
||||||
ServerProperties properties = this.serverProperties;
|
ServerProperties properties = this.serverProperties;
|
||||||
ServerProperties.Undertow undertowProperties = properties.getUndertow();
|
ServerProperties.Undertow undertowProperties = properties.getUndertow();
|
||||||
|
ServerProperties.Undertow.Options undertowOptions = undertowProperties.getOptions();
|
||||||
ServerProperties.Undertow.Accesslog accesslogProperties = undertowProperties.getAccesslog();
|
ServerProperties.Undertow.Accesslog accesslogProperties = undertowProperties.getAccesslog();
|
||||||
PropertyMapper propertyMapper = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
PropertyMapper propertyMapper = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||||
propertyMapper.from(undertowProperties::getBufferSize).whenNonNull().asInt(DataSize::toBytes)
|
propertyMapper.from(undertowProperties::getBufferSize).whenNonNull().asInt(DataSize::toBytes)
|
||||||
|
|
@ -109,6 +112,12 @@ public class UndertowWebServerFactoryCustomizer
|
||||||
.to((alwaysSetKeepAlive) -> customizeServerOption(factory, UndertowOptions.ALWAYS_SET_KEEP_ALIVE,
|
.to((alwaysSetKeepAlive) -> customizeServerOption(factory, UndertowOptions.ALWAYS_SET_KEEP_ALIVE,
|
||||||
alwaysSetKeepAlive));
|
alwaysSetKeepAlive));
|
||||||
|
|
||||||
|
propertyMapper.from(undertowOptions::getServer)
|
||||||
|
.to((server) -> server.forEach((key, value) -> setCustomOption(factory, key, value, "server")));
|
||||||
|
|
||||||
|
propertyMapper.from(undertowOptions::getSocket)
|
||||||
|
.to((socket) -> socket.forEach((key, value) -> setCustomOption(factory, key, value, "socket")));
|
||||||
|
|
||||||
factory.addDeploymentInfoCustomizers(
|
factory.addDeploymentInfoCustomizers(
|
||||||
(deploymentInfo) -> deploymentInfo.setEagerFilterInit(undertowProperties.isEagerFilterInit()));
|
(deploymentInfo) -> deploymentInfo.setEagerFilterInit(undertowProperties.isEagerFilterInit()));
|
||||||
}
|
}
|
||||||
|
|
@ -121,6 +130,10 @@ public class UndertowWebServerFactoryCustomizer
|
||||||
factory.addBuilderCustomizers((builder) -> builder.setServerOption(option, value));
|
factory.addBuilderCustomizers((builder) -> builder.setServerOption(option, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private <T> void customizeSocketOption(ConfigurableUndertowWebServerFactory factory, Option<T> option, T value) {
|
||||||
|
factory.addBuilderCustomizers((builder) -> builder.setSocketOption(option, value));
|
||||||
|
}
|
||||||
|
|
||||||
private boolean getOrDeduceUseForwardHeaders() {
|
private boolean getOrDeduceUseForwardHeaders() {
|
||||||
if (this.serverProperties.getForwardHeadersStrategy().equals(ServerProperties.ForwardHeadersStrategy.NONE)) {
|
if (this.serverProperties.getForwardHeadersStrategy().equals(ServerProperties.ForwardHeadersStrategy.NONE)) {
|
||||||
CloudPlatform platform = CloudPlatform.getActive(this.environment);
|
CloudPlatform platform = CloudPlatform.getActive(this.environment);
|
||||||
|
|
@ -129,4 +142,31 @@ public class UndertowWebServerFactoryCustomizer
|
||||||
return this.serverProperties.getForwardHeadersStrategy().equals(ServerProperties.ForwardHeadersStrategy.NATIVE);
|
return this.serverProperties.getForwardHeadersStrategy().equals(ServerProperties.ForwardHeadersStrategy.NATIVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private <T> void setCustomOption(ConfigurableUndertowWebServerFactory factory, String key, String value,
|
||||||
|
String type) {
|
||||||
|
Field[] fields = UndertowOptions.class.getDeclaredFields();
|
||||||
|
for (Field field : fields) {
|
||||||
|
if (getCanonicalName(field.getName()).equals(getCanonicalName(key))) {
|
||||||
|
Option<T> option = (Option<T>) Option.fromString(
|
||||||
|
UndertowOptions.class.getName() + '.' + field.getName(), getClass().getClassLoader());
|
||||||
|
T parsed = option.parseValue(value, getClass().getClassLoader());
|
||||||
|
if (type.equals("server")) {
|
||||||
|
customizeServerOption(factory, option, parsed);
|
||||||
|
}
|
||||||
|
else if (type.equals("socket")) {
|
||||||
|
customizeSocketOption(factory, option, parsed);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getCanonicalName(String key) {
|
||||||
|
StringBuilder canonicalName = new StringBuilder(key.length());
|
||||||
|
key.chars().map((c) -> (char) c).filter(Character::isLetterOrDigit).map(Character::toLowerCase)
|
||||||
|
.forEach((c) -> canonicalName.append((char) c));
|
||||||
|
return canonicalName.toString();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -214,6 +214,20 @@ class ServerPropertiesTests {
|
||||||
assertThat(this.properties.getJetty().getSelectors()).isEqualTo(10);
|
assertThat(this.properties.getJetty().getSelectors()).isEqualTo(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCustomizeUndertowServerOption() {
|
||||||
|
bind("server.undertow.options.server.ALWAYS_SET_KEEP_ALIVE", "true");
|
||||||
|
assertThat(this.properties.getUndertow().getOptions().getServer().containsKey("ALWAYS_SET_KEEP_ALIVE"));
|
||||||
|
assertThat(this.properties.getUndertow().getOptions().getServer().get("ALWAYS_SET_KEEP_ALIVE").equals("true"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testCustomizeUndertowSocketOption() {
|
||||||
|
bind("server.undertow.options.socket.ALWAYS_SET_KEEP_ALIVE", "true");
|
||||||
|
assertThat(this.properties.getUndertow().getOptions().getSocket().containsKey("ALWAYS_SET_KEEP_ALIVE"));
|
||||||
|
assertThat(this.properties.getUndertow().getOptions().getSocket().get("ALWAYS_SET_KEEP_ALIVE").equals("true"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testCustomizeJettyAccessLog() {
|
void testCustomizeJettyAccessLog() {
|
||||||
Map<String, String> map = new HashMap<>();
|
Map<String, String> map = new HashMap<>();
|
||||||
|
|
|
||||||
|
|
@ -155,6 +155,29 @@ class UndertowWebServerFactoryCustomizerTests {
|
||||||
assertThat(boundServerOption(UndertowOptions.ALWAYS_SET_KEEP_ALIVE)).isFalse();
|
assertThat(boundServerOption(UndertowOptions.ALWAYS_SET_KEEP_ALIVE)).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void customServerOption() {
|
||||||
|
bind("server.undertow.options.server.ALWAYS_SET_KEEP_ALIVE=false");
|
||||||
|
assertThat(boundServerOption(UndertowOptions.ALWAYS_SET_KEEP_ALIVE)).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void customServerOptionShouldBeRelaxed() {
|
||||||
|
bind("server.undertow.options.server.always-set-keep-alive=false");
|
||||||
|
assertThat(boundServerOption(UndertowOptions.ALWAYS_SET_KEEP_ALIVE)).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void customSocketOption() {
|
||||||
|
bind("server.undertow.options.socket.ALWAYS_SET_KEEP_ALIVE=false");
|
||||||
|
assertThat(boundSocketOption(UndertowOptions.ALWAYS_SET_KEEP_ALIVE)).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
void customSocketOptionShouldBeRelaxed() {
|
||||||
|
bind("server.undertow.options.socket.always-set-keep-alive=false");
|
||||||
|
assertThat(boundSocketOption(UndertowOptions.ALWAYS_SET_KEEP_ALIVE)).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void deduceUseForwardHeaders() {
|
void deduceUseForwardHeaders() {
|
||||||
this.environment.setProperty("DYNO", "-");
|
this.environment.setProperty("DYNO", "-");
|
||||||
|
|
@ -186,6 +209,14 @@ class UndertowWebServerFactoryCustomizerTests {
|
||||||
return map.get(option);
|
return map.get(option);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private <T> T boundSocketOption(Option<T> option) {
|
||||||
|
Builder builder = Undertow.builder();
|
||||||
|
ConfigurableUndertowWebServerFactory factory = mockFactory(builder);
|
||||||
|
this.customizer.customize(factory);
|
||||||
|
OptionMap map = ((OptionMap.Builder) ReflectionTestUtils.getField(builder, "socketOptions")).getMap();
|
||||||
|
return map.get(option);
|
||||||
|
}
|
||||||
|
|
||||||
private ConfigurableUndertowWebServerFactory mockFactory(Builder builder) {
|
private ConfigurableUndertowWebServerFactory mockFactory(Builder builder) {
|
||||||
ConfigurableUndertowWebServerFactory factory = mock(ConfigurableUndertowWebServerFactory.class);
|
ConfigurableUndertowWebServerFactory factory = mock(ConfigurableUndertowWebServerFactory.class);
|
||||||
willAnswer((invocation) -> {
|
willAnswer((invocation) -> {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue