Skip to content

Commit 5e3438f

Browse files
committed
Merge pull request #16535 from ayudovin
* pr/16535: Polish "Fix connection timeout configuration for Netty" Fix connection timeout configuration for Netty Chain predicates in PropertyMapper when methods Closes gh-16535
2 parents 88fbc52 + 692bda1 commit 5e3438f

File tree

4 files changed

+94
-13
lines changed

4 files changed

+94
-13
lines changed

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

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import org.springframework.boot.cloud.CloudPlatform;
2525
import org.springframework.boot.context.properties.PropertyMapper;
2626
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
27-
import org.springframework.boot.web.embedded.netty.NettyServerCustomizer;
2827
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
2928
import org.springframework.core.Ordered;
3029
import org.springframework.core.env.Environment;
@@ -58,11 +57,11 @@ public int getOrder() {
5857
@Override
5958
public void customize(NettyReactiveWebServerFactory factory) {
6059
factory.setUseForwardHeaders(getOrDeduceUseForwardHeaders(this.serverProperties, this.environment));
61-
PropertyMapper propertyMapper = PropertyMapper.get();
62-
propertyMapper.from(this.serverProperties::getMaxHttpHeaderSize).whenNonNull().asInt(DataSize::toBytes)
60+
PropertyMapper propertyMapper = PropertyMapper.get().alwaysApplyingWhenNonNull();
61+
propertyMapper.from(this.serverProperties::getMaxHttpHeaderSize)
6362
.to((maxHttpRequestHeaderSize) -> customizeMaxHttpHeaderSize(factory, maxHttpRequestHeaderSize));
64-
propertyMapper.from(this.serverProperties::getConnectionTimeout).whenNonNull().asInt(Duration::toMillis)
65-
.to((duration) -> factory.addServerCustomizers(getConnectionTimeOutCustomizer(duration)));
63+
propertyMapper.from(this.serverProperties::getConnectionTimeout)
64+
.to((connectionTimeout) -> customizeConnectionTimeout(factory, connectionTimeout));
6665
}
6766

6867
private boolean getOrDeduceUseForwardHeaders(ServerProperties serverProperties, Environment environment) {
@@ -73,14 +72,17 @@ private boolean getOrDeduceUseForwardHeaders(ServerProperties serverProperties,
7372
return platform != null && platform.isUsingForwardHeaders();
7473
}
7574

76-
private void customizeMaxHttpHeaderSize(NettyReactiveWebServerFactory factory, Integer maxHttpHeaderSize) {
77-
factory.addServerCustomizers((NettyServerCustomizer) (httpServer) -> httpServer.httpRequestDecoder(
78-
(httpRequestDecoderSpec) -> httpRequestDecoderSpec.maxHeaderSize(maxHttpHeaderSize)));
75+
private void customizeMaxHttpHeaderSize(NettyReactiveWebServerFactory factory, DataSize maxHttpHeaderSize) {
76+
factory.addServerCustomizers((httpServer) -> httpServer.httpRequestDecoder(
77+
(httpRequestDecoderSpec) -> httpRequestDecoderSpec.maxHeaderSize((int) maxHttpHeaderSize.toBytes())));
7978
}
8079

81-
private NettyServerCustomizer getConnectionTimeOutCustomizer(int duration) {
82-
return (httpServer) -> httpServer.tcpConfiguration(
83-
(tcpServer) -> tcpServer.selectorOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, duration));
80+
private void customizeConnectionTimeout(NettyReactiveWebServerFactory factory, Duration connectionTimeout) {
81+
if (!connectionTimeout.isZero()) {
82+
long timeoutMillis = connectionTimeout.isNegative() ? 0 : connectionTimeout.toMillis();
83+
factory.addServerCustomizers((httpServer) -> httpServer.tcpConfiguration((tcpServer) -> tcpServer
84+
.selectorOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, (int) timeoutMillis)));
85+
}
8486
}
8587

8688
}

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

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,38 @@
1616

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

19+
import java.time.Duration;
20+
import java.util.Map;
21+
22+
import io.netty.bootstrap.ServerBootstrap;
23+
import io.netty.channel.ChannelOption;
1924
import org.junit.Before;
2025
import org.junit.Test;
26+
import org.mockito.ArgumentCaptor;
27+
import org.mockito.Captor;
28+
import org.mockito.MockitoAnnotations;
29+
import reactor.netty.http.server.HttpServer;
30+
import reactor.netty.tcp.TcpServer;
2131

2232
import org.springframework.boot.autoconfigure.web.ServerProperties;
2333
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
2434
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
35+
import org.springframework.boot.web.embedded.netty.NettyServerCustomizer;
2536
import org.springframework.mock.env.MockEnvironment;
37+
import org.springframework.test.util.ReflectionTestUtils;
2638

39+
import static org.assertj.core.api.Assertions.assertThat;
40+
import static org.mockito.ArgumentMatchers.any;
2741
import static org.mockito.Mockito.mock;
42+
import static org.mockito.Mockito.never;
43+
import static org.mockito.Mockito.times;
2844
import static org.mockito.Mockito.verify;
2945

3046
/**
3147
* Tests for {@link NettyWebServerFactoryCustomizer}.
3248
*
3349
* @author Brian Clozel
50+
* @author Artsiom Yudovin
3451
*/
3552
public class NettyWebServerFactoryCustomizerTests {
3653

@@ -40,8 +57,12 @@ public class NettyWebServerFactoryCustomizerTests {
4057

4158
private NettyWebServerFactoryCustomizer customizer;
4259

60+
@Captor
61+
private ArgumentCaptor<NettyServerCustomizer> customizerCaptor;
62+
4363
@Before
4464
public void setup() {
65+
MockitoAnnotations.initMocks(this);
4566
this.environment = new MockEnvironment();
4667
this.serverProperties = new ServerProperties();
4768
ConfigurationPropertySources.attach(this.environment);
@@ -71,4 +92,49 @@ public void setUseForwardHeaders() {
7192
verify(factory).setUseForwardHeaders(true);
7293
}
7394

95+
@Test
96+
public void setConnectionTimeoutAsZero() {
97+
setupConnectionTimeout(Duration.ZERO);
98+
NettyReactiveWebServerFactory factory = mock(NettyReactiveWebServerFactory.class);
99+
this.customizer.customize(factory);
100+
verifyConnectionTimeout(factory, null);
101+
}
102+
103+
@Test
104+
public void setConnectionTimeoutAsMinusOne() {
105+
setupConnectionTimeout(Duration.ofNanos(-1));
106+
NettyReactiveWebServerFactory factory = mock(NettyReactiveWebServerFactory.class);
107+
this.customizer.customize(factory);
108+
verifyConnectionTimeout(factory, 0);
109+
}
110+
111+
@Test
112+
public void setConnectionTimeout() {
113+
setupConnectionTimeout(Duration.ofSeconds(1));
114+
NettyReactiveWebServerFactory factory = mock(NettyReactiveWebServerFactory.class);
115+
this.customizer.customize(factory);
116+
verifyConnectionTimeout(factory, 1000);
117+
}
118+
119+
@SuppressWarnings("unchecked")
120+
private void verifyConnectionTimeout(NettyReactiveWebServerFactory factory, Integer expected) {
121+
if (expected == null) {
122+
verify(factory, never()).addServerCustomizers(any(NettyServerCustomizer.class));
123+
return;
124+
}
125+
verify(factory, times(1)).addServerCustomizers(this.customizerCaptor.capture());
126+
NettyServerCustomizer serverCustomizer = this.customizerCaptor.getValue();
127+
HttpServer httpServer = serverCustomizer.apply(HttpServer.create());
128+
TcpServer tcpConfiguration = ReflectionTestUtils.invokeMethod(httpServer, "tcpConfiguration");
129+
ServerBootstrap bootstrap = tcpConfiguration.configure();
130+
Map<Object, Object> options = (Map<Object, Object>) ReflectionTestUtils.getField(bootstrap, "options");
131+
assertThat(options).containsEntry(ChannelOption.CONNECT_TIMEOUT_MILLIS, expected);
132+
}
133+
134+
private void setupConnectionTimeout(Duration connectionTimeout) {
135+
this.serverProperties.setUseForwardHeaders(null);
136+
this.serverProperties.setMaxHttpHeaderSize(null);
137+
this.serverProperties.setConnectionTimeout(connectionTimeout);
138+
}
139+
74140
}

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/PropertyMapper.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
* {@link Source#toInstance(Function) new instance}.
5151
*
5252
* @author Phillip Webb
53+
* @author Artsiom Yudovin
5354
* @since 2.0.0
5455
*/
5556
public final class PropertyMapper {
@@ -288,7 +289,7 @@ public <R extends T> Source<R> whenInstanceOf(Class<R> target) {
288289
*/
289290
public Source<T> whenNot(Predicate<T> predicate) {
290291
Assert.notNull(predicate, "Predicate must not be null");
291-
return new Source<>(this.supplier, predicate.negate());
292+
return when(predicate.negate());
292293
}
293294

294295
/**
@@ -299,7 +300,7 @@ public Source<T> whenNot(Predicate<T> predicate) {
299300
*/
300301
public Source<T> when(Predicate<T> predicate) {
301302
Assert.notNull(predicate, "Predicate must not be null");
302-
return new Source<>(this.supplier, predicate);
303+
return new Source<>(this.supplier, (this.predicate != null) ? this.predicate.and(predicate) : predicate);
303304
}
304305

305306
/**

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/properties/PropertyMapperTests.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
* Tests for {@link PropertyMapper}.
2929
*
3030
* @author Phillip Webb
31+
* @author Artsiom Yudovin
3132
*/
3233
public class PropertyMapperTests {
3334

@@ -190,6 +191,17 @@ public void whenWhenCombinedWithAsUsesSourceValue() {
190191
assertThat(source.getCount()).isOne();
191192
}
192193

194+
@Test
195+
public void whenWhenValueNotMatchesShouldSupportChainedCalls() {
196+
this.map.from("123").when("456"::equals).when("123"::equals).toCall(Assert::fail);
197+
}
198+
199+
@Test
200+
public void whenWhenValueMatchesShouldSupportChainedCalls() {
201+
String result = this.map.from("123").when((s) -> s.contains("2")).when("123"::equals).toInstance(String::new);
202+
assertThat(result).isEqualTo("123");
203+
}
204+
193205
@Test
194206
public void alwaysApplyingWhenNonNullShouldAlwaysApplyNonNullToSource() {
195207
this.map.alwaysApplyingWhenNonNull().from(() -> null).toCall(Assert::fail);

0 commit comments

Comments
 (0)