Allow lifecycleTimeout to be set for Netty
Update `NettyReactiveWebServerFactory` and `NettyWebServer` to allow the lifecycle (start/stop) timeout to be configured. Fixes gh-10977
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
package org.springframework.boot.web.embedded.netty;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@@ -42,6 +43,8 @@ public class NettyReactiveWebServerFactory extends AbstractReactiveWebServerFact
|
||||
|
||||
private List<NettyServerCustomizer> serverCustomizers = new ArrayList<>();
|
||||
|
||||
private Duration lifecycleTimeout;
|
||||
|
||||
public NettyReactiveWebServerFactory() {
|
||||
}
|
||||
|
||||
@@ -51,10 +54,10 @@ public class NettyReactiveWebServerFactory extends AbstractReactiveWebServerFact
|
||||
|
||||
@Override
|
||||
public WebServer getWebServer(HttpHandler httpHandler) {
|
||||
HttpServer server = createHttpServer();
|
||||
HttpServer httpServer = createHttpServer();
|
||||
ReactorHttpHandlerAdapter handlerAdapter = new ReactorHttpHandlerAdapter(
|
||||
httpHandler);
|
||||
return new NettyWebServer(server, handlerAdapter);
|
||||
return new NettyWebServer(httpServer, handlerAdapter, this.lifecycleTimeout);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,6 +89,15 @@ public class NettyReactiveWebServerFactory extends AbstractReactiveWebServerFact
|
||||
this.serverCustomizers.addAll(Arrays.asList(serverCustomizers));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum amount of time that should be waited when starting or stopping the
|
||||
* server.
|
||||
* @param lifecycleTimeout the lefecycle timeout
|
||||
*/
|
||||
public void setLifecycleTimeout(Duration lifecycleTimeout) {
|
||||
this.lifecycleTimeout = lifecycleTimeout;
|
||||
}
|
||||
|
||||
private HttpServer createHttpServer() {
|
||||
return HttpServer.builder().options((options) -> {
|
||||
options.listenAddress(getListenAddress());
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.boot.web.embedded.netty;
|
||||
import java.net.BindException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.time.Duration;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@@ -30,6 +31,7 @@ import org.springframework.boot.web.server.PortInUseException;
|
||||
import org.springframework.boot.web.server.WebServer;
|
||||
import org.springframework.boot.web.server.WebServerException;
|
||||
import org.springframework.http.server.reactive.ReactorHttpHandlerAdapter;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* {@link WebServer} that can be used to control a Reactor Netty web server. Usually this
|
||||
@@ -45,27 +47,32 @@ public class NettyWebServer implements WebServer {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(NettyWebServer.class);
|
||||
|
||||
private final HttpServer httpServer;
|
||||
|
||||
private final ReactorHttpHandlerAdapter handlerAdapter;
|
||||
|
||||
private final HttpServer reactorServer;
|
||||
private final Duration lifecycleTimeout;
|
||||
|
||||
private BlockingNettyContext nettyContext;
|
||||
|
||||
public NettyWebServer(HttpServer reactorServer,
|
||||
ReactorHttpHandlerAdapter handlerAdapter) {
|
||||
this.reactorServer = reactorServer;
|
||||
public NettyWebServer(HttpServer httpServer, ReactorHttpHandlerAdapter handlerAdapter,
|
||||
Duration lifecycleTimeout) {
|
||||
Assert.notNull(httpServer, "HttpServer must not be null");
|
||||
Assert.notNull(handlerAdapter, "HandlerAdapter must not be null");
|
||||
this.httpServer = httpServer;
|
||||
this.handlerAdapter = handlerAdapter;
|
||||
this.lifecycleTimeout = lifecycleTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() throws WebServerException {
|
||||
if (this.nettyContext == null) {
|
||||
try {
|
||||
this.nettyContext = this.reactorServer.start(this.handlerAdapter);
|
||||
this.nettyContext = startHttpServer();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
if (findBindException(ex) != null) {
|
||||
SocketAddress address = this.reactorServer.options().getAddress();
|
||||
SocketAddress address = this.httpServer.options().getAddress();
|
||||
if (address instanceof InetSocketAddress) {
|
||||
throw new PortInUseException(
|
||||
((InetSocketAddress) address).getPort());
|
||||
@@ -78,6 +85,13 @@ public class NettyWebServer implements WebServer {
|
||||
}
|
||||
}
|
||||
|
||||
private BlockingNettyContext startHttpServer() {
|
||||
if (this.lifecycleTimeout != null) {
|
||||
return this.httpServer.start(this.handlerAdapter, this.lifecycleTimeout);
|
||||
}
|
||||
return this.httpServer.start(this.handlerAdapter);
|
||||
}
|
||||
|
||||
private BindException findBindException(Exception ex) {
|
||||
Throwable candidate = ex;
|
||||
while (candidate != null) {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.boot.web.embedded.netty;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
@@ -25,7 +26,9 @@ import reactor.ipc.netty.http.server.HttpServerOptions;
|
||||
import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactory;
|
||||
import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFactoryTests;
|
||||
import org.springframework.boot.web.server.PortInUseException;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
@@ -72,4 +75,16 @@ public class NettyReactiveWebServerFactoryTests
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customStartupTimeout() {
|
||||
Duration timeout = Duration.ofDays(365);
|
||||
NettyReactiveWebServerFactory factory = getFactory();
|
||||
factory.setLifecycleTimeout(timeout);
|
||||
this.webServer = factory.getWebServer(new EchoHandler());
|
||||
this.webServer.start();
|
||||
Object context = ReflectionTestUtils.getField(this.webServer, "nettyContext");
|
||||
Object actualTimeout = ReflectionTestUtils.getField(context, "lifecycleTimeout");
|
||||
assertThat(actualTimeout).isEqualTo(timeout);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user