Merge branch '2.2.x'

Closes gh-21105
This commit is contained in:
Phillip Webb 2020-04-23 14:44:08 -07:00
commit 49345025ac
7 changed files with 84 additions and 77 deletions

View File

@ -17,7 +17,6 @@
package org.springframework.boot.web.embedded.jetty;
import java.io.IOException;
import java.net.BindException;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
@ -174,8 +173,9 @@ public class JettyWebServer implements WebServer {
connector.start();
}
catch (IOException ex) {
if (connector instanceof NetworkConnector && findBindException(ex) != null) {
throw new PortInUseException(((NetworkConnector) connector).getPort(), ex);
if (connector instanceof NetworkConnector) {
PortInUseException.throwIfPortBindingException(ex,
() -> ((NetworkConnector) connector).getPort());
}
throw ex;
}
@ -195,16 +195,6 @@ public class JettyWebServer implements WebServer {
}
}
private BindException findBindException(Throwable ex) {
if (ex == null) {
return null;
}
if (ex instanceof BindException) {
return (BindException) ex;
}
return findBindException(ex.getCause());
}
private String getActualPortsDescription() {
StringBuilder ports = new StringBuilder();
for (Connector connector : this.server.getConnectors()) {

View File

@ -116,10 +116,11 @@ public class NettyWebServer implements WebServer {
this.disposableServer = startHttpServer();
}
catch (Exception ex) {
ChannelBindException bindException = findBindException(ex);
if (bindException != null && !isPermissionDenied(bindException.getCause())) {
throw new PortInUseException(bindException.localPort(), ex);
}
PortInUseException.ifCausedBy(ex, ChannelBindException.class, (bindException) -> {
if (!isPermissionDenied(bindException.getCause())) {
throw new PortInUseException(bindException.localPort(), ex);
}
});
throw new WebServerException("Unable to start Netty", ex);
}
logger.info("Netty started on port(s): " + getPort());
@ -168,17 +169,6 @@ public class NettyWebServer implements WebServer {
routes.route(ALWAYS, this.handler);
}
private ChannelBindException findBindException(Exception ex) {
Throwable candidate = ex;
while (candidate != null) {
if (candidate instanceof ChannelBindException) {
return (ChannelBindException) candidate;
}
candidate = candidate.getCause();
}
return null;
}
private void startDaemonAwaitThread(DisposableServer disposableServer) {
Thread awaitThread = new Thread("server") {

View File

@ -16,7 +16,6 @@
package org.springframework.boot.web.embedded.tomcat;
import java.net.BindException;
import java.time.Duration;
import java.util.Arrays;
import java.util.HashMap;
@ -226,9 +225,7 @@ public class TomcatWebServer implements WebServer {
throw ex;
}
catch (Exception ex) {
if (findBindException(ex) != null) {
throw new PortInUseException(this.tomcat.getConnector().getPort());
}
PortInUseException.throwIfPortBindingException(ex, () -> this.tomcat.getConnector().getPort());
throw new WebServerException("Unable to start embedded Tomcat server", ex);
}
finally {
@ -251,16 +248,6 @@ public class TomcatWebServer implements WebServer {
}
}
private BindException findBindException(Throwable ex) {
if (ex == null) {
return null;
}
if (ex instanceof BindException) {
return (BindException) ex;
}
return findBindException(ex.getCause());
}
private void stopSilently() {
try {
stopTomcat();

View File

@ -17,7 +17,6 @@
package org.springframework.boot.web.embedded.undertow;
import java.lang.reflect.Field;
import java.net.BindException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.time.Duration;
@ -173,14 +172,13 @@ public class UndertowServletWebServer implements WebServer {
}
catch (Exception ex) {
try {
if (findBindException(ex) != null) {
PortInUseException.ifPortBindingException(ex, (bindException) -> {
List<Port> failedPorts = getConfiguredPorts();
List<Port> actualPorts = getActualPorts();
failedPorts.removeAll(actualPorts);
failedPorts.removeAll(getActualPorts());
if (failedPorts.size() == 1) {
throw new PortInUseException(failedPorts.iterator().next().getNumber(), ex);
throw new PortInUseException(failedPorts.get(0).getNumber());
}
}
});
throw new WebServerException("Unable to start embedded Undertow", ex);
}
finally {
@ -207,17 +205,6 @@ public class UndertowServletWebServer implements WebServer {
}
}
private BindException findBindException(Exception ex) {
Throwable candidate = ex;
while (candidate != null) {
if (candidate instanceof BindException) {
return (BindException) candidate;
}
candidate = candidate.getCause();
}
return null;
}
private Undertow createUndertowServer() throws ServletException {
HttpHandler httpHandler = this.manager.start();
httpHandler = getContextHandler(httpHandler);

View File

@ -18,7 +18,6 @@ package org.springframework.boot.web.embedded.undertow;
import java.io.Closeable;
import java.lang.reflect.Field;
import java.net.BindException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.ArrayList;
@ -121,14 +120,13 @@ public class UndertowWebServer implements WebServer {
}
catch (Exception ex) {
try {
if (findBindException(ex) != null) {
List<UndertowWebServer.Port> failedPorts = getConfiguredPorts();
List<UndertowWebServer.Port> actualPorts = getActualPorts();
failedPorts.removeAll(actualPorts);
PortInUseException.ifPortBindingException(ex, (bindException) -> {
List<Port> failedPorts = getConfiguredPorts();
failedPorts.removeAll(getActualPorts());
if (failedPorts.size() == 1) {
throw new PortInUseException(failedPorts.iterator().next().getNumber(), ex);
throw new PortInUseException(failedPorts.get(0).getNumber());
}
}
});
throw new WebServerException("Unable to start embedded Undertow", ex);
}
finally {
@ -150,17 +148,6 @@ public class UndertowWebServer implements WebServer {
}
}
private BindException findBindException(Exception ex) {
Throwable candidate = ex;
while (candidate != null) {
if (candidate instanceof BindException) {
return (BindException) candidate;
}
candidate = candidate.getCause();
}
return null;
}
private String getPortsDescription() {
List<UndertowWebServer.Port> ports = getActualPorts();
if (!ports.isEmpty()) {

View File

@ -16,11 +16,16 @@
package org.springframework.boot.web.server;
import java.net.BindException;
import java.util.function.Consumer;
import java.util.function.IntSupplier;
/**
* A {@code PortInUseException} is thrown when a web server fails to start due to a port
* already being in use.
*
* @author Andy Wilkinson
* @author Phillip Webb
* @since 2.0.0
*/
public class PortInUseException extends WebServerException {
@ -53,4 +58,53 @@ public class PortInUseException extends WebServerException {
return this.port;
}
/**
* Throw a {@link PortInUseException} if the given exception was caused by a "port in
* use" {@link BindException}.
* @param ex the source exception
* @param port a suppler used to provide the port
* @since 2.2.7
*/
public static void throwIfPortBindingException(Exception ex, IntSupplier port) {
ifPortBindingException(ex, (bindException) -> {
throw new PortInUseException(port.getAsInt(), ex);
});
}
/**
* Perform an action if the given exception was caused by a "port in use"
* {@link BindException}.
* @param ex the source exception
* @param action the action to perform
* @since 2.2.7
*/
public static void ifPortBindingException(Exception ex, Consumer<BindException> action) {
ifCausedBy(ex, BindException.class, (bindException) -> {
// bind exception can be also thrown because an address can't be assigned
if (bindException.getMessage().toLowerCase().contains("in use")) {
action.accept(bindException);
}
});
}
/**
* Perform an action if the given exception was caused by a specific exception type.
* @param <E> the cause exception type
* @param ex the source exception
* @param causedBy the required cause type
* @param action the action to perform
* @since 2.2.7
*/
@SuppressWarnings("unchecked")
public static <E extends Exception> void ifCausedBy(Exception ex, Class<E> causedBy, Consumer<E> action) {
Throwable candidate = ex;
while (candidate != null) {
if (causedBy.isInstance(candidate)) {
action.accept((E) candidate);
return;
}
candidate = candidate.getCause();
}
}
}

View File

@ -22,6 +22,7 @@ import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.ServerSocket;
@ -112,6 +113,7 @@ import org.springframework.boot.testsupport.web.servlet.ExampleServlet;
import org.springframework.boot.web.server.Compression;
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.MimeMappings;
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.Ssl.ClientAuth;
@ -919,6 +921,16 @@ public abstract class AbstractServletWebServerFactoryTests {
});
}
@Test
void malformedAddress() throws Exception {
AbstractServletWebServerFactory factory = getFactory();
factory.setAddress(InetAddress.getByName("123456"));
assertThatExceptionOfType(RuntimeException.class).isThrownBy(() -> {
this.webServer = factory.getWebServer();
this.webServer.start();
}).isNotInstanceOf(PortInUseException.class);
}
@Test
void localeCharsetMappingsAreConfigured() {
AbstractServletWebServerFactory factory = getFactory();