Close Channel upon exceptions during bootstrap to avoid leaking (#10110)

Motivation:

We need to ensure we always close the Channel when we see an exception during bootstrapping it.

Modification:

Ensure we correct close the Channel if we see an exception during retrieving the Resolver from the group while bootstrapping.

Result:

Fixes #10109


Co-authored-by: Norman Maurer <norman_maurer@apple.com>
This commit is contained in:
Bo Zhang 2020-03-16 20:37:29 +08:00 committed by GitHub
parent c74469bc6c
commit 286f14f04a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 39 additions and 3 deletions

View File

@ -189,7 +189,13 @@ public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> {
final SocketAddress localAddress, final ChannelPromise promise) {
try {
final EventLoop eventLoop = channel.eventLoop();
final AddressResolver<SocketAddress> resolver = this.resolver.getResolver(eventLoop);
AddressResolver<SocketAddress> resolver;
try {
resolver = this.resolver.getResolver(eventLoop);
} catch (Throwable cause) {
channel.close();
return promise.setFailure(cause);
}
if (!resolver.isSupported(remoteAddress) || resolver.isResolved(remoteAddress)) {
// Resolver has no idea about what to do with the specified remote address or it's resolved already.

View File

@ -30,7 +30,6 @@ import io.netty.channel.ChannelPromise;
import io.netty.channel.DefaultChannelConfig;
import io.netty.channel.DefaultEventLoop;
import io.netty.channel.DefaultEventLoopGroup;
import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.ServerChannel;
import io.netty.channel.local.LocalAddress;
@ -52,7 +51,6 @@ import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
@ -278,6 +276,38 @@ public class BootstrapTest {
assertThat(connectFuture.channel().isOpen(), is(false));
}
@Test
public void testGetResolverFailed() throws Exception {
class TestException extends RuntimeException { }
final Bootstrap bootstrapA = new Bootstrap();
bootstrapA.group(groupA);
bootstrapA.channel(LocalChannel.class);
bootstrapA.resolver(new AddressResolverGroup<SocketAddress>() {
@Override
protected AddressResolver<SocketAddress> newResolver(EventExecutor executor) {
throw new TestException();
}
});
bootstrapA.handler(dummyHandler);
final ServerBootstrap bootstrapB = new ServerBootstrap();
bootstrapB.group(groupB);
bootstrapB.channel(LocalServerChannel.class);
bootstrapB.childHandler(dummyHandler);
SocketAddress localAddress = bootstrapB.bind(LocalAddress.ANY).sync().channel().localAddress();
// Connect to the server using the asynchronous resolver.
ChannelFuture connectFuture = bootstrapA.connect(localAddress);
// Should fail with the IllegalStateException.
assertThat(connectFuture.await(10000), is(true));
assertThat(connectFuture.cause(), instanceOf(IllegalStateException.class));
assertThat(connectFuture.cause().getCause(), instanceOf(TestException.class));
assertThat(connectFuture.channel().isOpen(), is(false));
}
@Test
public void testChannelFactoryFailureNotifiesPromise() throws Exception {
final RuntimeException exception = new RuntimeException("newChannel crash");