From d55b51aaa835769742696a5942e2d3930d71eea3 Mon Sep 17 00:00:00 2001 From: Kyle Thomson Date: Fri, 17 Nov 2017 10:34:14 -0800 Subject: [PATCH] Adding socket resolver helper that will load the appopriate SocketChannel for known implementations of EventLoopGroup --- .../nio/netty/NettyNioAsyncHttpClient.java | 23 +------ .../internal/utils/SocketChannelResolver.java | 65 +++++++++++++++++++ .../utils/SocketChannelResolverTest.java | 39 +++++++++++ 3 files changed, 106 insertions(+), 21 deletions(-) create mode 100644 http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/SocketChannelResolver.java create mode 100644 http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/utils/SocketChannelResolverTest.java diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClient.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClient.java index 5d8f06665506..86706f2dd05f 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClient.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClient.java @@ -16,25 +16,21 @@ package software.amazon.awssdk.http.nio.netty; import static io.netty.handler.ssl.SslContext.defaultClientProvider; +import static software.amazon.awssdk.http.nio.netty.internal.utils.SocketChannelResolver.resolveSocketChannelClass; import static software.amazon.awssdk.utils.FunctionalUtils.invokeSafely; import io.netty.bootstrap.Bootstrap; -import io.netty.channel.Channel; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; -import io.netty.channel.epoll.EpollEventLoopGroup; -import io.netty.channel.epoll.EpollSocketChannel; import io.netty.channel.pool.ChannelHealthChecker; import io.netty.channel.pool.ChannelPool; import io.netty.channel.pool.ChannelPoolMap; import io.netty.channel.pool.FixedChannelPool; -import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import java.net.URI; import java.util.Optional; -import software.amazon.awssdk.annotations.ReviewBeforeRelease; import software.amazon.awssdk.annotations.SdkInternalApi; import software.amazon.awssdk.http.SdkHttpConfigurationOption; import software.amazon.awssdk.http.SdkHttpRequest; @@ -44,7 +40,6 @@ import software.amazon.awssdk.http.async.SdkHttpRequestProvider; import software.amazon.awssdk.http.async.SdkHttpResponseHandler; import software.amazon.awssdk.http.nio.netty.internal.ChannelPipelineInitializer; -import software.amazon.awssdk.http.nio.netty.internal.DelegatingEventLoopGroup; import software.amazon.awssdk.http.nio.netty.internal.NettyConfiguration; import software.amazon.awssdk.http.nio.netty.internal.NonManagedEventLoopGroup; import software.amazon.awssdk.http.nio.netty.internal.RequestAdapter; @@ -78,7 +73,7 @@ protected ChannelPool newPool(URI key) { Bootstrap bootstrap = new Bootstrap() .group(group) - .channel(resolveSocketChannelClass()) + .channel(resolveSocketChannelClass(group)) .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, configuration.connectionTimeout()) .option(ChannelOption.TCP_NODELAY, true) .remoteAddress(key.getHost(), key.getPort()); @@ -117,20 +112,6 @@ public void close() { group.shutdownGracefully(); } - /** - * Depending on the EventLoopGroup used we may need to use a different socket channel. - */ - @ReviewBeforeRelease("Perhaps we should make the customer provide both event loop group" + - "and channel in some kind of wrapper class to avoid having to do this.") - private Class resolveSocketChannelClass() { - EventLoopGroup unwrapped = group; - // Keep unwrapping until it's not a DelegatingEventLoopGroup - while (unwrapped instanceof DelegatingEventLoopGroup) { - unwrapped = ((DelegatingEventLoopGroup) unwrapped).getDelegate(); - } - return unwrapped instanceof EpollEventLoopGroup ? EpollSocketChannel.class : NioSocketChannel.class; - } - private static URI poolKey(SdkHttpRequest sdkRequest) { return invokeSafely(() -> new URI(sdkRequest.protocol(), null, sdkRequest.host(), sdkRequest.port(), null, null, null)); diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/SocketChannelResolver.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/SocketChannelResolver.java new file mode 100644 index 000000000000..17b95a1cc07c --- /dev/null +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/SocketChannelResolver.java @@ -0,0 +1,65 @@ +/* + * Copyright 2010-2017 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.utils; + +import static software.amazon.awssdk.utils.FunctionalUtils.invokeSafely; + +import io.netty.channel.Channel; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.epoll.EpollEventLoopGroup; +import io.netty.channel.epoll.EpollSocketChannel; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioSocketChannel; +import java.util.HashMap; +import java.util.Map; +import software.amazon.awssdk.http.nio.netty.internal.DelegatingEventLoopGroup; + +public final class SocketChannelResolver { + + private static final Map KNOWN_EL_GROUPS = new HashMap<>(); + + static { + KNOWN_EL_GROUPS.put("io.netty.channel.kqueue.KQueueEventLoopGroup", "io.netty.channel.kqueue.KQueueSocketChannel"); + KNOWN_EL_GROUPS.put("io.netty.channel.oio.OioEventLoopGroup", "io.netty.channel.socket.oio.OioSocketChannel"); + } + + private SocketChannelResolver() { + } + + /** + * Attempts to determine the {@link Channel} class that corresponds to the given + * event loop group. + * + * @param eventLoopGroup the event loop group to determine the {@link Channel} for + * @return A {@link Channel} class for the given event loop group. + */ + public static Class resolveSocketChannelClass(EventLoopGroup eventLoopGroup) { + if (eventLoopGroup instanceof DelegatingEventLoopGroup) { + return resolveSocketChannelClass(((DelegatingEventLoopGroup) eventLoopGroup).getDelegate()); + } + if (eventLoopGroup instanceof NioEventLoopGroup) { + return NioSocketChannel.class; + } + if (eventLoopGroup instanceof EpollEventLoopGroup) { + return EpollSocketChannel.class; + } + String socketFqcn = KNOWN_EL_GROUPS.get(eventLoopGroup.getClass().getName()); + if (socketFqcn == null) { + throw new IllegalArgumentException("Unknown event loop group : " + eventLoopGroup.getClass()); + } + return invokeSafely(() -> (Class) Class.forName(socketFqcn)); + } +} diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/utils/SocketChannelResolverTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/utils/SocketChannelResolverTest.java new file mode 100644 index 000000000000..37077b32523f --- /dev/null +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/utils/SocketChannelResolverTest.java @@ -0,0 +1,39 @@ +package software.amazon.awssdk.http.nio.netty.internal.utils; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assume.assumeTrue; +import static software.amazon.awssdk.http.nio.netty.internal.utils.SocketChannelResolver.resolveSocketChannelClass; + +import io.netty.channel.epoll.Epoll; +import io.netty.channel.epoll.EpollEventLoopGroup; +import io.netty.channel.epoll.EpollSocketChannel; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.oio.OioEventLoopGroup; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.channel.socket.oio.OioSocketChannel; +import org.junit.Test; +import software.amazon.awssdk.http.nio.netty.internal.DelegatingEventLoopGroup; + +public class SocketChannelResolverTest { + + @Test + public void canDetectForStandardNioEventLoopGroup() { + assertThat(resolveSocketChannelClass(new NioEventLoopGroup())).isEqualTo(NioSocketChannel.class); + } + + @Test + public void canDetectEpollEventLoopGroup() { + assumeTrue(Epoll.isAvailable()); + assertThat(resolveSocketChannelClass(new EpollEventLoopGroup())).isEqualTo(EpollSocketChannel.class); + } + + @Test + public void worksWithDelegateEventLoopGroups() { + assertThat(resolveSocketChannelClass(new DelegatingEventLoopGroup(new NioEventLoopGroup()) {})).isEqualTo(NioSocketChannel.class); + } + + @Test + public void worksWithOioEventLoopGroup() { + assertThat(resolveSocketChannelClass(new OioEventLoopGroup())).isEqualTo(OioSocketChannel.class); + } +} \ No newline at end of file