diff --git a/src/main/java/reactor/netty/tcp/DisposableServerBindException.java b/src/main/java/reactor/netty/tcp/DisposableServerBindException.java new file mode 100644 index 0000000000..37393e383b --- /dev/null +++ b/src/main/java/reactor/netty/tcp/DisposableServerBindException.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2011-2018 Pivotal Software Inc, All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package reactor.netty.tcp; + +import java.net.SocketAddress; + +/** + * An extended {@link java.net.BindException} that provides information + * for the local address. + * + * @author Violeta Georgieva + */ +public class DisposableServerBindException extends RuntimeException { + final SocketAddress localAddress; + + DisposableServerBindException(String message, SocketAddress localAddress) { + super(message); + this.localAddress = localAddress; + } + + @Override + public synchronized Throwable fillInStackTrace() { + return this; + } + + public SocketAddress getLocalAddress() { + return localAddress; + } +} diff --git a/src/main/java/reactor/netty/tcp/TcpServerBind.java b/src/main/java/reactor/netty/tcp/TcpServerBind.java index fbdf1d11c1..846911aaf0 100644 --- a/src/main/java/reactor/netty/tcp/TcpServerBind.java +++ b/src/main/java/reactor/netty/tcp/TcpServerBind.java @@ -17,6 +17,7 @@ package reactor.netty.tcp; import java.io.IOException; +import java.net.BindException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.Objects; @@ -83,7 +84,7 @@ public Mono bind(ServerBootstrap b) { ChannelFuture f = bootstrap.bind(); - DisposableBind disposableServer = new DisposableBind(sink, f, obs); + DisposableBind disposableServer = new DisposableBind(sink, f, obs, b.config().localAddress()); f.addListener(disposableServer); sink.onCancel(disposableServer); }); @@ -142,12 +143,14 @@ static final class DisposableBind final MonoSink sink; final ChannelFuture f; final ConnectionObserver selectorObserver; + final SocketAddress localAddress; DisposableBind(MonoSink sink, ChannelFuture f, - ConnectionObserver selectorObserver) { + ConnectionObserver selectorObserver, SocketAddress localAddress) { this.sink = sink; this.f = f; this.selectorObserver = selectorObserver; + this.localAddress = localAddress; } @Override @@ -179,8 +182,12 @@ public final void operationComplete(ChannelFuture f) { } return; } - if (f.cause() != null) { - sink.error(f.cause()); + Throwable t = f.cause(); + if (t != null) { + if (t instanceof BindException) { + t = new DisposableServerBindException(t.getMessage() + " " + localAddress, localAddress); + } + sink.error(t); } else { sink.error(new IOException("error while binding to " + f.channel())); diff --git a/src/test/java/reactor/netty/http/server/HttpServerTests.java b/src/test/java/reactor/netty/http/server/HttpServerTests.java index 4c2e9632d8..21cd7b027a 100644 --- a/src/test/java/reactor/netty/http/server/HttpServerTests.java +++ b/src/test/java/reactor/netty/http/server/HttpServerTests.java @@ -58,6 +58,7 @@ import reactor.netty.http.HttpResources; import reactor.netty.http.client.HttpClient; import reactor.netty.resources.ConnectionProvider; +import reactor.netty.tcp.DisposableServerBindException; import reactor.netty.tcp.TcpClient; import reactor.test.StepVerifier; import reactor.util.context.Context; @@ -66,6 +67,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.fail; /** * @author Stephane Maldini @@ -864,4 +866,29 @@ private Object getValueReflection(Object obj, String fieldName, int superLevel) return null; } } + + @Test + public void testIssue368() { + DisposableServer server = + HttpServer.create() + .port(8080) + .bindNow(); + assertThat(server).isNotNull(); + + try { + HttpServer.create() + .port(8080) + .bindNow(); + fail("The bind operation should fail"); + } + catch (DisposableServerBindException e) { + assertThat(e.getMessage()).contains("Address already in use") + .contains("8080"); + } + catch (Throwable t) { + fail("DisposableServerBindException is expected ", t); + } + + server.dispose(); + } }