Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

Expand Down Expand Up @@ -1119,6 +1120,8 @@ public static class Undertow {

private final Accesslog accesslog = new Accesslog();

private final Options options = new Options();

public DataSize getMaxHttpPostSize() {
return this.maxHttpPostSize;
}
Expand Down Expand Up @@ -1227,6 +1230,10 @@ public Accesslog getAccesslog() {
return this.accesslog;
}

public Options getOptions() {
return this.options;
}

/**
* Undertow access log properties.
*/
Expand Down Expand Up @@ -1312,6 +1319,22 @@ public void setRotate(boolean rotate) {

}

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;
}

}

}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package org.springframework.boot.autoconfigure.web.embedded;

import java.lang.reflect.Field;

import io.undertow.UndertowOptions;
import org.xnio.Option;

Expand Down Expand Up @@ -61,6 +63,7 @@ public int getOrder() {
public void customize(ConfigurableUndertowWebServerFactory factory) {
ServerProperties properties = this.serverProperties;
ServerProperties.Undertow undertowProperties = properties.getUndertow();
ServerProperties.Undertow.Options undertowOptions = undertowProperties.getOptions();
ServerProperties.Undertow.Accesslog accesslogProperties = undertowProperties.getAccesslog();
PropertyMapper propertyMapper = PropertyMapper.get().alwaysApplyingWhenNonNull();
propertyMapper.from(undertowProperties::getBufferSize).whenNonNull().asInt(DataSize::toBytes)
Expand Down Expand Up @@ -109,6 +112,12 @@ public void customize(ConfigurableUndertowWebServerFactory factory) {
.to((alwaysSetKeepAlive) -> customizeServerOption(factory, UndertowOptions.ALWAYS_SET_KEEP_ALIVE,
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(
(deploymentInfo) -> deploymentInfo.setEagerFilterInit(undertowProperties.isEagerFilterInit()));
}
Expand All @@ -121,6 +130,10 @@ private <T> void customizeServerOption(ConfigurableUndertowWebServerFactory fact
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() {
if (this.serverProperties.getForwardHeadersStrategy().equals(ServerProperties.ForwardHeadersStrategy.NONE)) {
CloudPlatform platform = CloudPlatform.getActive(this.environment);
Expand All @@ -129,4 +142,31 @@ private boolean getOrDeduceUseForwardHeaders() {
return this.serverProperties.getForwardHeadersStrategy().equals(ServerProperties.ForwardHeadersStrategy.NATIVE);
}

private <T> void setCustomOption(ConfigurableUndertowWebServerFactory factory, String key, String value,
String type) {
Field[] fields = UndertowOptions.class.getDeclaredFields();
for (Field field : fields) {
String name = getLetterAndNumber(key);
if (getLetterAndNumber(field.getName()).equals(name)) {
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 getLetterAndNumber(String name) {
StringBuilder canonicalName = new StringBuilder(name.length());
name.chars().map((c) -> (char) c).filter(Character::isLetterOrDigit).map(Character::toLowerCase)
.forEach((c) -> canonicalName.append((char) c));
return canonicalName.toString();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,20 @@ void testCustomizeJettySelectors() {
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
void testCustomizeJettyAccessLog() {
Map<String, String> map = new HashMap<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,18 @@ void disableAlwaysSetKeepAlive() {
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 customSocketOption() {
bind("server.undertow.options.socket.ALWAYS_SET_KEEP_ALIVE=false");
assertThat(boundSocketOption(UndertowOptions.ALWAYS_SET_KEEP_ALIVE)).isFalse();
}

@Test
void deduceUseForwardHeaders() {
this.environment.setProperty("DYNO", "-");
Expand Down Expand Up @@ -186,6 +198,14 @@ private <T> T boundServerOption(Option<T> 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) {
ConfigurableUndertowWebServerFactory factory = mock(ConfigurableUndertowWebServerFactory.class);
willAnswer((invocation) -> {
Expand Down