diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/netty/NettyWebServer.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/netty/NettyWebServer.java index 236c7b39929..b4360ea1e22 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/netty/NettyWebServer.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/netty/NettyWebServer.java @@ -64,6 +64,11 @@ public class NettyWebServer implements WebServer { */ private static final int ERROR_NO_EACCES = -13; + /** + * Address in use error code from {@code errno.h}. + */ + private static final int ERROR_ADDR_IN_USE = -98; + private static final Predicate ALWAYS = (request) -> true; private static final Log logger = LogFactory.getLog(NettyWebServer.class); @@ -115,11 +120,19 @@ public class NettyWebServer implements WebServer { this.disposableServer = startHttpServer(); } catch (Exception ex) { - PortInUseException.ifCausedBy(ex, ChannelBindException.class, (bindException) -> { - if (bindException.localPort() > 0 && !isPermissionDenied(bindException.getCause())) { - throw new PortInUseException(bindException.localPort(), ex); + PortInUseException.ifCausedBy(ex, ChannelBindException.class, (channelBindException) -> { + if (channelBindException.localPort() > 0 && !isPermissionDenied(channelBindException.getCause())) { + PortInUseException.throwIfPortBindingException(channelBindException, + channelBindException::localPort); } }); + if (ex instanceof ChannelBindException channelBindException) { + PortInUseException.ifCausedBy(ex, NativeIoException.class, (nativeIoException) -> { + if (nativeIoException.expectedErr() == ERROR_ADDR_IN_USE) { + throw new PortInUseException(channelBindException.localPort(), ex); + } + }); + } throw new WebServerException("Unable to start Netty", ex); } if (this.disposableServer != null) { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/netty/NettyReactiveWebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/netty/NettyReactiveWebServerFactoryTests.java index cb03e6efb0e..fa09e787b6b 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/netty/NettyReactiveWebServerFactoryTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/netty/NettyReactiveWebServerFactoryTests.java @@ -17,7 +17,9 @@ package org.springframework.boot.web.embedded.netty; import java.net.ConnectException; +import java.net.InetAddress; import java.net.SocketAddress; +import java.net.UnknownHostException; import java.time.Duration; import java.util.Arrays; @@ -45,6 +47,7 @@ import org.springframework.boot.web.reactive.server.AbstractReactiveWebServerFac import org.springframework.boot.web.server.PortInUseException; import org.springframework.boot.web.server.Shutdown; import org.springframework.boot.web.server.Ssl; +import org.springframework.boot.web.server.WebServerException; import org.springframework.http.MediaType; import org.springframework.http.client.ReactorResourceFactory; import org.springframework.http.client.reactive.ReactorClientHttpConnector; @@ -70,7 +73,7 @@ import static org.mockito.Mockito.mock; class NettyReactiveWebServerFactoryTests extends AbstractReactiveWebServerFactoryTests { @Test - void exceptionIsThrownWhenPortIsAlreadyInUse() { + void portInUseExceptionIsThrownWhenPortIsAlreadyInUse() { AbstractReactiveWebServerFactory factory = getFactory(); factory.setPort(0); this.webServer = factory.getWebServer(new EchoHandler()); @@ -81,6 +84,14 @@ class NettyReactiveWebServerFactoryTests extends AbstractReactiveWebServerFactor .withCauseInstanceOf(Throwable.class); } + @Test + void webServerExceptionIsThrownWhenAddressCannotBeAssigned() throws UnknownHostException { + AbstractReactiveWebServerFactory factory = getFactory(); + factory.setPort(8080); + factory.setAddress(InetAddress.getByName("1.2.3.4")); + assertThatExceptionOfType(WebServerException.class).isThrownBy(factory.getWebServer(new EchoHandler())::start); + } + @Test void getPortWhenDisposableServerPortOperationIsUnsupportedReturnsMinusOne() { NettyReactiveWebServerFactory factory = new NoPortNettyReactiveWebServerFactory(0);