Skip to content

Commit c19bed1

Browse files
committed
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
2 parents f0e934e + 3bd7760 commit c19bed1

File tree

4 files changed

+108
-0
lines changed

4 files changed

+108
-0
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.time.temporal.ChronoUnit;
2525
import java.util.ArrayList;
2626
import java.util.HashMap;
27+
import java.util.LinkedHashMap;
2728
import java.util.List;
2829
import java.util.Map;
2930

@@ -1119,6 +1120,8 @@ public static class Undertow {
11191120

11201121
private final Accesslog accesslog = new Accesslog();
11211122

1123+
private final Options options = new Options();
1124+
11221125
public DataSize getMaxHttpPostSize() {
11231126
return this.maxHttpPostSize;
11241127
}
@@ -1227,6 +1230,10 @@ public Accesslog getAccesslog() {
12271230
return this.accesslog;
12281231
}
12291232

1233+
public Options getOptions() {
1234+
return this.options;
1235+
}
1236+
12301237
/**
12311238
* Undertow access log properties.
12321239
*/
@@ -1312,6 +1319,22 @@ public void setRotate(boolean rotate) {
13121319

13131320
}
13141321

1322+
public static class Options {
1323+
1324+
private Map<String, String> socket = new LinkedHashMap<>();
1325+
1326+
private Map<String, String> server = new LinkedHashMap<>();
1327+
1328+
public Map<String, String> getServer() {
1329+
return this.server;
1330+
}
1331+
1332+
public Map<String, String> getSocket() {
1333+
return this.socket;
1334+
}
1335+
1336+
}
1337+
13151338
}
13161339

13171340
/**

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/embedded/UndertowWebServerFactoryCustomizer.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

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

19+
import java.lang.reflect.Field;
20+
1921
import io.undertow.UndertowOptions;
2022
import org.xnio.Option;
2123

@@ -61,6 +63,7 @@ public int getOrder() {
6163
public void customize(ConfigurableUndertowWebServerFactory factory) {
6264
ServerProperties properties = this.serverProperties;
6365
ServerProperties.Undertow undertowProperties = properties.getUndertow();
66+
ServerProperties.Undertow.Options undertowOptions = undertowProperties.getOptions();
6467
ServerProperties.Undertow.Accesslog accesslogProperties = undertowProperties.getAccesslog();
6568
PropertyMapper propertyMapper = PropertyMapper.get().alwaysApplyingWhenNonNull();
6669
propertyMapper.from(undertowProperties::getBufferSize).whenNonNull().asInt(DataSize::toBytes)
@@ -109,6 +112,12 @@ public void customize(ConfigurableUndertowWebServerFactory factory) {
109112
.to((alwaysSetKeepAlive) -> customizeServerOption(factory, UndertowOptions.ALWAYS_SET_KEEP_ALIVE,
110113
alwaysSetKeepAlive));
111114

115+
propertyMapper.from(undertowOptions::getServer)
116+
.to((server) -> server.forEach((key, value) -> setCustomOption(factory, key, value, "server")));
117+
118+
propertyMapper.from(undertowOptions::getSocket)
119+
.to((socket) -> socket.forEach((key, value) -> setCustomOption(factory, key, value, "socket")));
120+
112121
factory.addDeploymentInfoCustomizers(
113122
(deploymentInfo) -> deploymentInfo.setEagerFilterInit(undertowProperties.isEagerFilterInit()));
114123
}
@@ -121,6 +130,10 @@ private <T> void customizeServerOption(ConfigurableUndertowWebServerFactory fact
121130
factory.addBuilderCustomizers((builder) -> builder.setServerOption(option, value));
122131
}
123132

133+
private <T> void customizeSocketOption(ConfigurableUndertowWebServerFactory factory, Option<T> option, T value) {
134+
factory.addBuilderCustomizers((builder) -> builder.setSocketOption(option, value));
135+
}
136+
124137
private boolean getOrDeduceUseForwardHeaders() {
125138
if (this.serverProperties.getForwardHeadersStrategy().equals(ServerProperties.ForwardHeadersStrategy.NONE)) {
126139
CloudPlatform platform = CloudPlatform.getActive(this.environment);
@@ -129,4 +142,31 @@ private boolean getOrDeduceUseForwardHeaders() {
129142
return this.serverProperties.getForwardHeadersStrategy().equals(ServerProperties.ForwardHeadersStrategy.NATIVE);
130143
}
131144

145+
@SuppressWarnings("unchecked")
146+
private <T> void setCustomOption(ConfigurableUndertowWebServerFactory factory, String key, String value,
147+
String type) {
148+
Field[] fields = UndertowOptions.class.getDeclaredFields();
149+
for (Field field : fields) {
150+
if (getCanonicalName(field.getName()).equals(getCanonicalName(key))) {
151+
Option<T> option = (Option<T>) Option.fromString(
152+
UndertowOptions.class.getName() + '.' + field.getName(), getClass().getClassLoader());
153+
T parsed = option.parseValue(value, getClass().getClassLoader());
154+
if (type.equals("server")) {
155+
customizeServerOption(factory, option, parsed);
156+
}
157+
else if (type.equals("socket")) {
158+
customizeSocketOption(factory, option, parsed);
159+
}
160+
return;
161+
}
162+
}
163+
}
164+
165+
private String getCanonicalName(String key) {
166+
StringBuilder canonicalName = new StringBuilder(key.length());
167+
key.chars().map((c) -> (char) c).filter(Character::isLetterOrDigit).map(Character::toLowerCase)
168+
.forEach((c) -> canonicalName.append((char) c));
169+
return canonicalName.toString();
170+
}
171+
132172
}

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesTests.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,20 @@ void testCustomizeJettySelectors() {
214214
assertThat(this.properties.getJetty().getSelectors()).isEqualTo(10);
215215
}
216216

217+
@Test
218+
void testCustomizeUndertowServerOption() {
219+
bind("server.undertow.options.server.ALWAYS_SET_KEEP_ALIVE", "true");
220+
assertThat(this.properties.getUndertow().getOptions().getServer().containsKey("ALWAYS_SET_KEEP_ALIVE"));
221+
assertThat(this.properties.getUndertow().getOptions().getServer().get("ALWAYS_SET_KEEP_ALIVE").equals("true"));
222+
}
223+
224+
@Test
225+
void testCustomizeUndertowSocketOption() {
226+
bind("server.undertow.options.socket.ALWAYS_SET_KEEP_ALIVE", "true");
227+
assertThat(this.properties.getUndertow().getOptions().getSocket().containsKey("ALWAYS_SET_KEEP_ALIVE"));
228+
assertThat(this.properties.getUndertow().getOptions().getSocket().get("ALWAYS_SET_KEEP_ALIVE").equals("true"));
229+
}
230+
217231
@Test
218232
void testCustomizeJettyAccessLog() {
219233
Map<String, String> map = new HashMap<>();

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/embedded/UndertowWebServerFactoryCustomizerTests.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,29 @@ void disableAlwaysSetKeepAlive() {
155155
assertThat(boundServerOption(UndertowOptions.ALWAYS_SET_KEEP_ALIVE)).isFalse();
156156
}
157157

158+
@Test
159+
void customServerOption() {
160+
bind("server.undertow.options.server.ALWAYS_SET_KEEP_ALIVE=false");
161+
assertThat(boundServerOption(UndertowOptions.ALWAYS_SET_KEEP_ALIVE)).isFalse();
162+
}
163+
164+
@Test
165+
void customServerOptionShouldBeRelaxed() {
166+
bind("server.undertow.options.server.always-set-keep-alive=false");
167+
assertThat(boundServerOption(UndertowOptions.ALWAYS_SET_KEEP_ALIVE)).isFalse();
168+
}
169+
170+
@Test
171+
void customSocketOption() {
172+
bind("server.undertow.options.socket.ALWAYS_SET_KEEP_ALIVE=false");
173+
assertThat(boundSocketOption(UndertowOptions.ALWAYS_SET_KEEP_ALIVE)).isFalse();
174+
}
175+
176+
void customSocketOptionShouldBeRelaxed() {
177+
bind("server.undertow.options.socket.always-set-keep-alive=false");
178+
assertThat(boundSocketOption(UndertowOptions.ALWAYS_SET_KEEP_ALIVE)).isFalse();
179+
}
180+
158181
@Test
159182
void deduceUseForwardHeaders() {
160183
this.environment.setProperty("DYNO", "-");
@@ -186,6 +209,14 @@ private <T> T boundServerOption(Option<T> option) {
186209
return map.get(option);
187210
}
188211

212+
private <T> T boundSocketOption(Option<T> option) {
213+
Builder builder = Undertow.builder();
214+
ConfigurableUndertowWebServerFactory factory = mockFactory(builder);
215+
this.customizer.customize(factory);
216+
OptionMap map = ((OptionMap.Builder) ReflectionTestUtils.getField(builder, "socketOptions")).getMap();
217+
return map.get(option);
218+
}
219+
189220
private ConfigurableUndertowWebServerFactory mockFactory(Builder builder) {
190221
ConfigurableUndertowWebServerFactory factory = mock(ConfigurableUndertowWebServerFactory.class);
191222
willAnswer((invocation) -> {

0 commit comments

Comments
 (0)