From e4afa9e5865c8d5d832186cdf258d0e7734902d0 Mon Sep 17 00:00:00 2001 From: Stephane Maldini Date: Tue, 24 Jul 2018 05:31:41 -0700 Subject: [PATCH] Add explicit bind exception (#398) Remove unused code --- .../reactor/netty/ChannelBindException.java | 84 +++++++++++++++++++ .../http/server/HttpServerConfiguration.java | 2 - .../resources/NewConnectionProvider.java | 40 +++------ .../java/reactor/netty/tcp/TcpServerBind.java | 16 ++-- .../netty/http/server/HttpServerTests.java | 20 +++++ 5 files changed, 124 insertions(+), 38 deletions(-) create mode 100644 src/main/java/reactor/netty/ChannelBindException.java diff --git a/src/main/java/reactor/netty/ChannelBindException.java b/src/main/java/reactor/netty/ChannelBindException.java new file mode 100644 index 0000000000..2b81a4776e --- /dev/null +++ b/src/main/java/reactor/netty/ChannelBindException.java @@ -0,0 +1,84 @@ +/* + * 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; + +import java.net.InetSocketAddress; +import java.util.Objects; + +import io.netty.bootstrap.AbstractBootstrap; +import reactor.util.annotation.Nullable; + +/** + * Represents a failing attempt to bind a local socket address + * + * @author Stephane Maldini + */ +public class ChannelBindException extends RuntimeException { + + /** + * Build a {@link ChannelBindException} + * + * @param bootstrap a netty bootstrap + * @param cause a root cause + * + * @return a new {@link ChannelBindException} + */ + public static ChannelBindException fail(AbstractBootstrap bootstrap, @Nullable Throwable cause) { + Objects.requireNonNull(bootstrap, "bootstrap"); + if (cause instanceof java.net.BindException) { + cause = null; + } + if (!(bootstrap.config().localAddress() instanceof InetSocketAddress)) { + return new ChannelBindException(bootstrap.config().localAddress().toString(), -1, cause); + } + InetSocketAddress address = (InetSocketAddress)bootstrap.config().localAddress(); + + return new ChannelBindException(address.getHostString(), address.getPort(), cause); + } + + final String localHost; + final int localPort; + + protected ChannelBindException(String localHost, int localPort, @Nullable Throwable cause) { + super("Failed to bind on ["+localHost+":"+localPort+"]", cause); + this.localHost = localHost; + this.localPort = localPort; + } + + @Override + public synchronized Throwable fillInStackTrace() { + return this; + } + + /** + * Return the configured binding host + * + * @return the configured binding host + */ + public String localHost() { + return localHost; + } + + /** + * Return the configured binding port + * + * @return the configured local binding port + */ + public int localPort() { + return localPort; + } +} diff --git a/src/main/java/reactor/netty/http/server/HttpServerConfiguration.java b/src/main/java/reactor/netty/http/server/HttpServerConfiguration.java index 23fd884476..29ad424a8f 100644 --- a/src/main/java/reactor/netty/http/server/HttpServerConfiguration.java +++ b/src/main/java/reactor/netty/http/server/HttpServerConfiguration.java @@ -33,8 +33,6 @@ final class HttpServerConfiguration { static final AttributeKey CONF_KEY = AttributeKey.newInstance("httpServerConf"); - static final HttpProtocol[] HTTP11 = {HttpProtocol.HTTP11}; - BiPredicate compressPredicate = null; int minCompressionSize = -1; diff --git a/src/main/java/reactor/netty/resources/NewConnectionProvider.java b/src/main/java/reactor/netty/resources/NewConnectionProvider.java index 5d220e140d..aba588cf7f 100644 --- a/src/main/java/reactor/netty/resources/NewConnectionProvider.java +++ b/src/main/java/reactor/netty/resources/NewConnectionProvider.java @@ -17,17 +17,18 @@ package reactor.netty.resources; import java.io.IOException; +import java.net.BindException; import java.net.SocketAddress; import java.util.Objects; import java.util.function.Supplier; import io.netty.bootstrap.Bootstrap; -import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import reactor.core.Disposable; import reactor.core.publisher.Mono; import reactor.core.publisher.MonoSink; +import reactor.netty.ChannelBindException; import reactor.netty.Connection; import reactor.netty.ConnectionObserver; import reactor.netty.channel.BootstrapHandlers; @@ -70,7 +71,7 @@ public Mono acquire(Bootstrap b) { else { f = bootstrap.bind(); } - DisposableConnect disposableConnect = new DisposableConnect(sink, f); + DisposableConnect disposableConnect = new DisposableConnect(sink, f, bootstrap); f.addListener(disposableConnect); sink.onCancel(disposableConnect); }); @@ -103,11 +104,14 @@ static final class DisposableConnect final MonoSink sink; final ChannelFuture f; + final Bootstrap bootstrap; - DisposableConnect(MonoSink sink, ChannelFuture f) { + DisposableConnect(MonoSink sink, ChannelFuture f, Bootstrap + bootstrap) { this.sink = sink; this.f = f; + this.bootstrap = bootstrap; } @Override @@ -138,7 +142,12 @@ public final void operationComplete(ChannelFuture f) { return; } if (f.cause() != null) { - sink.error(f.cause()); + if (f.cause() instanceof BindException) { + sink.error(ChannelBindException.fail(bootstrap, f.cause())); + } + else { + sink.error(f.cause()); + } } else { sink.error(new IOException("error while connecting to " + f.channel())); @@ -153,29 +162,6 @@ public final void operationComplete(ChannelFuture f) { } } - static final class NewConnection implements Connection { - final Channel channel; - - NewConnection(Channel channel) { - this.channel = channel; - } - - @Override - public Channel channel() { - return channel; - } - - @Override - public boolean isPersistent() { - return false; - } - - @Override - public String toString() { - return "NewConnection{" + "channel=" + channel + '}'; - } - } - static final class NewConnectionObserver implements ConnectionObserver { diff --git a/src/main/java/reactor/netty/tcp/TcpServerBind.java b/src/main/java/reactor/netty/tcp/TcpServerBind.java index fbdf1d11c1..35537fa972 100644 --- a/src/main/java/reactor/netty/tcp/TcpServerBind.java +++ b/src/main/java/reactor/netty/tcp/TcpServerBind.java @@ -16,7 +16,6 @@ package reactor.netty.tcp; -import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.Objects; @@ -32,6 +31,7 @@ import reactor.core.Disposable; import reactor.core.publisher.Mono; import reactor.core.publisher.MonoSink; +import reactor.netty.ChannelBindException; import reactor.netty.Connection; import reactor.netty.ConnectionObserver; import reactor.netty.DisposableServer; @@ -83,7 +83,7 @@ public Mono bind(ServerBootstrap b) { ChannelFuture f = bootstrap.bind(); - DisposableBind disposableServer = new DisposableBind(sink, f, obs); + DisposableBind disposableServer = new DisposableBind(sink, f, obs, bootstrap); f.addListener(disposableServer); sink.onCancel(disposableServer); }); @@ -141,11 +141,14 @@ static final class DisposableBind final MonoSink sink; final ChannelFuture f; + final ServerBootstrap bootstrap; final ConnectionObserver selectorObserver; DisposableBind(MonoSink sink, ChannelFuture f, - ConnectionObserver selectorObserver) { + ConnectionObserver selectorObserver, + ServerBootstrap bootstrap) { this.sink = sink; + this.bootstrap = bootstrap; this.f = f; this.selectorObserver = selectorObserver; } @@ -179,12 +182,7 @@ public final void operationComplete(ChannelFuture f) { } return; } - if (f.cause() != null) { - sink.error(f.cause()); - } - else { - sink.error(new IOException("error while binding to " + f.channel())); - } + sink.error(ChannelBindException.fail(bootstrap, f.cause())); } else { if (log.isDebugEnabled()) { diff --git a/src/test/java/reactor/netty/http/server/HttpServerTests.java b/src/test/java/reactor/netty/http/server/HttpServerTests.java index 4c2e9632d8..7bb991e401 100644 --- a/src/test/java/reactor/netty/http/server/HttpServerTests.java +++ b/src/test/java/reactor/netty/http/server/HttpServerTests.java @@ -52,6 +52,7 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.netty.ByteBufFlux; +import reactor.netty.ChannelBindException; import reactor.netty.Connection; import reactor.netty.DisposableServer; import reactor.netty.FutureMono; @@ -779,6 +780,25 @@ public void testIssue309() { .httpRequestDecoder(c -> c.maxInitialLineLength(20))); } + @Test + public void portBindingException() { + DisposableServer d = HttpServer.create() + .port(0) + .bindNow(); + + try { + HttpServer.create() + .port(d.port()) + .bindNow(); + Assert.fail("illegal-success"); + } + catch (ChannelBindException e){ + Assert.assertEquals(e.localPort(), d.port()); + e.printStackTrace(); + } + d.dispose(); + } + private void doTestIssue309(String path, HttpServer httpServer) { DisposableServer server = httpServer.handle((req, res) -> res.sendString(Mono.just("Should not be reached")))