diff --git a/.changes/next-release/feature-AWSSDKforJavav2-a75db63.json b/.changes/next-release/feature-AWSSDKforJavav2-a75db63.json new file mode 100644 index 000000000000..b557210ac9d4 --- /dev/null +++ b/.changes/next-release/feature-AWSSDKforJavav2-a75db63.json @@ -0,0 +1,6 @@ +{ + "type": "feature", + "category": "Netty NIO HTTP Client", + "contributor": "", + "description": "Bump Netty to 4.2.2" +} diff --git a/bom-internal/pom.xml b/bom-internal/pom.xml index 9f6f8aa9c93d..5c32a76091de 100644 --- a/bom-internal/pom.xml +++ b/bom-internal/pom.xml @@ -116,7 +116,7 @@ io.netty - netty-codec + netty-codec-base ${netty.version} diff --git a/bundle-sdk/pom.xml b/bundle-sdk/pom.xml index 20941eb8ac28..027c377f3c4a 100644 --- a/bundle-sdk/pom.xml +++ b/bundle-sdk/pom.xml @@ -108,7 +108,7 @@ io.netty - netty-codec + netty-codec-base io.netty diff --git a/http-clients/netty-nio-client/pom.xml b/http-clients/netty-nio-client/pom.xml index 8b25166548bb..f92adaa3974c 100644 --- a/http-clients/netty-nio-client/pom.xml +++ b/http-clients/netty-nio-client/pom.xml @@ -63,7 +63,7 @@ io.netty - netty-codec + netty-codec-base io.netty diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/SdkEventLoopGroup.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/SdkEventLoopGroup.java index 254211e9303f..e19ae0c28f75 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/SdkEventLoopGroup.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/SdkEventLoopGroup.java @@ -18,7 +18,9 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelFactory; import io.netty.channel.EventLoopGroup; +import io.netty.channel.MultiThreadIoEventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.nio.NioIoHandler; import io.netty.channel.socket.DatagramChannel; import io.netty.channel.socket.nio.NioDatagramChannel; import io.netty.channel.socket.nio.NioSocketChannel; @@ -32,7 +34,7 @@ /** * Provides {@link EventLoopGroup} and {@link ChannelFactory} for {@link NettyNioAsyncHttpClient}. *

- * There are three ways to create a new instance. + * There are four ways to create a new instance. * *

* *

@@ -76,6 +81,16 @@ public final class SdkEventLoopGroup { this.datagramChannelFactory = ChannelResolver.resolveDatagramChannelFactory(eventLoopGroup); } + SdkEventLoopGroup(EventLoopGroup eventLoopGroup, ChannelFactory socketChannelFactory, + ChannelFactory datagramChannelFactory) { + Validate.paramNotNull(eventLoopGroup, "eventLoopGroup"); + Validate.paramNotNull(socketChannelFactory, "socketChannelFactory"); + Validate.paramNotNull(datagramChannelFactory, "datagramChannelFactory"); + this.eventLoopGroup = eventLoopGroup; + this.channelFactory = socketChannelFactory; + this.datagramChannelFactory = datagramChannelFactory; + } + /** * Create an instance of {@link SdkEventLoopGroup} from the builder */ @@ -107,15 +122,30 @@ public ChannelFactory datagramChannelFactory() { } /** - * Creates a new instance of SdkEventLoopGroup with {@link EventLoopGroup} and {@link ChannelFactory} + * Creates a new instance of SdkEventLoopGroup with {@link EventLoopGroup} and socket {@link ChannelFactory} * to be used with {@link NettyNioAsyncHttpClient}. * * @param eventLoopGroup the EventLoopGroup to be used - * @param channelFactory the channel factor to be used + * @param socketChannelFactory the socket channel factory to be used + * @return a new instance of SdkEventLoopGroup + */ + public static SdkEventLoopGroup create(EventLoopGroup eventLoopGroup, + ChannelFactory socketChannelFactory) { + return new SdkEventLoopGroup(eventLoopGroup, socketChannelFactory); + } + + /** + * Creates a new instance of SdkEventLoopGroup with {@link EventLoopGroup}, and socket and datagram + * {@link ChannelFactory}'s to be used with {@link NettyNioAsyncHttpClient}. + * + * @param eventLoopGroup the EventLoopGroup to be used + * @param socketChannelFactory the socket channel factory to be used + * @param datagramChannelFactory the datagram channel factory to be used * @return a new instance of SdkEventLoopGroup */ - public static SdkEventLoopGroup create(EventLoopGroup eventLoopGroup, ChannelFactory channelFactory) { - return new SdkEventLoopGroup(eventLoopGroup, channelFactory); + public static SdkEventLoopGroup create(EventLoopGroup eventLoopGroup, ChannelFactory socketChannelFactory, + ChannelFactory datagramChannelFactory) { + return new SdkEventLoopGroup(eventLoopGroup, socketChannelFactory, datagramChannelFactory); } /** @@ -125,6 +155,17 @@ public static SdkEventLoopGroup create(EventLoopGroup eventLoopGroup, ChannelFac * {@link ChannelFactory} will be resolved based on the type of {@link EventLoopGroup} provided. IllegalArgumentException will * be thrown for any unknown EventLoopGroup type. * + *

+ * Special handling for {@link MultiThreadIoEventLoopGroup}: + * When a {@link MultiThreadIoEventLoopGroup} is provided (not the deprecated transport-specific event loop groups like + * {@link NioEventLoopGroup}) the SDK cannot determine which transport type was configured and will default to using + * {@link NioSocketChannel} and {@link NioDatagramChannel}. + * + *

+ * To use {@link MultiThreadIoEventLoopGroup} with non-NIO transports (such as Epoll or KQueue), + * use {@link #create(EventLoopGroup, ChannelFactory, ChannelFactory)} and explicitly specify + * the desired socket and datagram channel factories. + * * @param eventLoopGroup the EventLoopGroup to be used * @return a new instance of SdkEventLoopGroup */ @@ -142,7 +183,7 @@ private EventLoopGroup resolveEventLoopGroup(DefaultBuilder builder) { .orElseGet(() -> new ThreadFactoryBuilder() .threadNamePrefix("aws-java-sdk-NettyEventLoop") .build()); - return new NioEventLoopGroup(numThreads, threadFactory); + return new MultiThreadIoEventLoopGroup(numThreads, threadFactory, NioIoHandler.newFactory()); /* Need to investigate why epoll is raising channel inactive after successful response that causes problems with retries. diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/ChannelResolver.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/ChannelResolver.java index 8770d683a679..05c21d4e9cec 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/ChannelResolver.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/ChannelResolver.java @@ -20,6 +20,7 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelFactory; import io.netty.channel.EventLoopGroup; +import io.netty.channel.MultiThreadIoEventLoopGroup; import io.netty.channel.ReflectiveChannelFactory; import io.netty.channel.epoll.EpollDatagramChannel; import io.netty.channel.epoll.EpollEventLoopGroup; @@ -75,11 +76,17 @@ public static ChannelFactory resolveSocketChannelFactory(Even } String socketFqcn = KNOWN_EL_GROUPS_SOCKET_CHANNELS.get(eventLoopGroup.getClass().getName()); - if (socketFqcn == null) { - throw new IllegalArgumentException("Unknown event loop group : " + eventLoopGroup.getClass()); + if (socketFqcn != null) { + return invokeSafely(() -> new ReflectiveChannelFactory(Class.forName(socketFqcn))); } - return invokeSafely(() -> new ReflectiveChannelFactory(Class.forName(socketFqcn))); + // The old deprecated transport-specific event loop groups all extend MultiThreadIoEventLoopGroup, so we have to do + // this check last. It is not possible to determine the type of transport factory so we will default to Nio. + if (eventLoopGroup instanceof MultiThreadIoEventLoopGroup) { + return NioSocketChannel::new; + } + + throw new IllegalArgumentException("Unknown event loop group : " + eventLoopGroup.getClass()); } /** @@ -103,10 +110,16 @@ public static ChannelFactory resolveDatagramChannelFa } String datagramFqcn = KNOWN_EL_GROUPS_DATAGRAM_CHANNELS.get(eventLoopGroup.getClass().getName()); - if (datagramFqcn == null) { - throw new IllegalArgumentException("Unknown event loop group : " + eventLoopGroup.getClass()); + if (datagramFqcn != null) { + return invokeSafely(() -> new ReflectiveChannelFactory(Class.forName(datagramFqcn))); + } + + // The old deprecated transport-specific event loop groups all extend MultiThreadIoEventLoopGroup, so we have to do + // this check last. It is not possible to determine the type of transport factory so we will default to Nio. + if (eventLoopGroup instanceof MultiThreadIoEventLoopGroup) { + return NioDatagramChannel::new; } - return invokeSafely(() -> new ReflectiveChannelFactory(Class.forName(datagramFqcn))); + throw new IllegalArgumentException("Unknown event loop group : " + eventLoopGroup.getClass()); } } diff --git a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/NettyUtils.java b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/NettyUtils.java index 6a9d0eedb682..f832c0f90751 100644 --- a/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/NettyUtils.java +++ b/http-clients/netty-nio-client/src/main/java/software/amazon/awssdk/http/nio/netty/internal/utils/NettyUtils.java @@ -336,7 +336,6 @@ public static SslHandler newSslHandler(SslContext sslContext, ByteBufAllocator a */ private static void configureSslEngine(SSLEngine sslEngine) { SSLParameters sslParameters = sslEngine.getSSLParameters(); - sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); sslEngine.setSSLParameters(sslParameters); } diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/SdkEventLoopGroupTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/SdkEventLoopGroupTest.java index bb2598345cff..7f26b5ebfdc0 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/SdkEventLoopGroupTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/SdkEventLoopGroupTest.java @@ -16,18 +16,23 @@ package software.amazon.awssdk.http.nio.netty; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; import io.netty.channel.DefaultEventLoopGroup; +import io.netty.channel.epoll.Epoll; import io.netty.channel.epoll.EpollDatagramChannel; -import io.netty.channel.epoll.EpollEventLoopGroup; +import io.netty.channel.epoll.EpollIoHandler; import io.netty.channel.epoll.EpollSocketChannel; +import io.netty.channel.MultiThreadIoEventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.nio.NioIoHandler; import io.netty.channel.oio.OioEventLoopGroup; import io.netty.channel.socket.nio.NioDatagramChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.channel.socket.oio.OioDatagramChannel; import io.netty.channel.socket.oio.OioSocketChannel; -import org.junit.Test; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.Test; public class SdkEventLoopGroupTest { @@ -64,8 +69,42 @@ public void notProvidingChannelFactory_channelFactoryResolved() { assertThat(sdkEventLoopGroup.datagramChannelFactory().newChannel()).isInstanceOf(NioDatagramChannel.class); } - @Test(expected = IllegalArgumentException.class) + @Test + public void multiThreadIoEventLoopGroup_nioIoHandler_withoutChannelFactory() { + SdkEventLoopGroup sdkEventLoopGroup = + SdkEventLoopGroup.create(new MultiThreadIoEventLoopGroup(NioIoHandler.newFactory())); + + assertThat(sdkEventLoopGroup.channelFactory().newChannel()).isInstanceOf(NioSocketChannel.class); + assertThat(sdkEventLoopGroup.datagramChannelFactory().newChannel()).isInstanceOf(NioDatagramChannel.class); + } + + @Test + public void multiThreadIoEventLoopGroupWithNonNioIoHandler_withoutChannelFactory_createsNioChannels() { + Assumptions.assumeTrue(Epoll.isAvailable()); + + SdkEventLoopGroup sdkEventLoopGroup = + SdkEventLoopGroup.create(new MultiThreadIoEventLoopGroup(EpollIoHandler.newFactory())); + + assertThat(sdkEventLoopGroup.channelFactory().newChannel()).isInstanceOf(NioSocketChannel.class); + assertThat(sdkEventLoopGroup.datagramChannelFactory().newChannel()).isInstanceOf(NioDatagramChannel.class); + } + + @Test + public void multiThreadIoEventLoopGroupWithNonNioIoHandler_withChannelsFactories_properlySetsChannelTypes() { + Assumptions.assumeTrue(Epoll.isAvailable()); + + SdkEventLoopGroup sdkEventLoopGroup = + SdkEventLoopGroup.create(new MultiThreadIoEventLoopGroup(EpollIoHandler.newFactory()), + EpollSocketChannel::new, + EpollDatagramChannel::new); + assertThat(sdkEventLoopGroup.channelFactory()).isNotNull(); + assertThat(sdkEventLoopGroup.channelFactory().newChannel()).isInstanceOf(EpollSocketChannel.class); + assertThat(sdkEventLoopGroup.datagramChannelFactory().newChannel()).isInstanceOf(EpollDatagramChannel.class); + assertThat(sdkEventLoopGroup.eventLoopGroup()).isNotNull(); + } + + @Test public void notProvidingChannelFactory_unknownEventLoopGroup() { - SdkEventLoopGroup.create(new DefaultEventLoopGroup()); + assertThrows(IllegalArgumentException.class, () -> SdkEventLoopGroup.create(new DefaultEventLoopGroup())); } } diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/fault/ServerConnectivityErrorMessageTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/fault/ServerConnectivityErrorMessageTest.java index 70857614033b..e7258a9d63f8 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/fault/ServerConnectivityErrorMessageTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/fault/ServerConnectivityErrorMessageTest.java @@ -29,8 +29,10 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.MultiThreadIoEventLoopGroup; import io.netty.channel.SimpleChannelInboundHandler; -import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.nio.NioIoHandler; import io.netty.channel.socket.ServerSocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.http.DefaultHttpContent; @@ -312,7 +314,7 @@ public RequestParams build(){ } private static class Server extends ChannelInitializer { - private final NioEventLoopGroup group = new NioEventLoopGroup(); + private final EventLoopGroup group = new MultiThreadIoEventLoopGroup(NioIoHandler.newFactory()); private CloseTime closeTime; private ServerBootstrap bootstrap; private ServerSocketChannel serverSock; diff --git a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/utils/ChannelResolverTest.java b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/utils/ChannelResolverTest.java index 45edd2b81bb1..4fe4e82fd1bf 100644 --- a/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/utils/ChannelResolverTest.java +++ b/http-clients/netty-nio-client/src/test/java/software/amazon/awssdk/http/nio/netty/internal/utils/ChannelResolverTest.java @@ -19,11 +19,13 @@ import static software.amazon.awssdk.http.nio.netty.internal.utils.ChannelResolver.resolveDatagramChannelFactory; import static software.amazon.awssdk.http.nio.netty.internal.utils.ChannelResolver.resolveSocketChannelFactory; +import io.netty.channel.MultiThreadIoEventLoopGroup; import io.netty.channel.epoll.Epoll; import io.netty.channel.epoll.EpollDatagramChannel; import io.netty.channel.epoll.EpollEventLoopGroup; import io.netty.channel.epoll.EpollSocketChannel; import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.nio.NioIoHandler; import io.netty.channel.oio.OioEventLoopGroup; import io.netty.channel.socket.nio.NioDatagramChannel; import io.netty.channel.socket.nio.NioSocketChannel; @@ -41,6 +43,12 @@ public void canDetectFactoryForStandardNioEventLoopGroup() { assertThat(resolveDatagramChannelFactory(new NioEventLoopGroup()).newChannel()).isInstanceOf(NioDatagramChannel.class); } + @Test + public void multiThreadIoEventLoopGroup_returnsNioChannels() { + assertThat(resolveSocketChannelFactory(new MultiThreadIoEventLoopGroup(NioIoHandler.newFactory())).newChannel()).isInstanceOf(NioSocketChannel.class); + assertThat(resolveDatagramChannelFactory(new MultiThreadIoEventLoopGroup(NioIoHandler.newFactory())).newChannel()).isInstanceOf(NioDatagramChannel.class); + } + @Test public void canDetectEpollEventLoopGroupFactory() { Assumptions.assumeTrue(Epoll.isAvailable()); diff --git a/pom.xml b/pom.xml index db2b809a4276..ca4f61c83a0d 100644 --- a/pom.xml +++ b/pom.xml @@ -115,7 +115,7 @@ 3.15.1 - 4.1.118.Final + 4.2.2.Final 3.4.6 1.3 UTF-8 @@ -139,7 +139,7 @@ 1.1 7.1.0 2.6 - 2.0.70.Final + 2.0.72.Final 1.25.0 1.0.392 1.0.8.RELEASE diff --git a/test/http-client-tests/pom.xml b/test/http-client-tests/pom.xml index 8a17b6a70453..e8a8371d98fd 100644 --- a/test/http-client-tests/pom.xml +++ b/test/http-client-tests/pom.xml @@ -120,8 +120,8 @@ org.bouncycastle - bcpkix-jdk15on - 1.70 + bcpkix-jdk18on + 1.80 compile diff --git a/test/protocol-tests/pom.xml b/test/protocol-tests/pom.xml index 1e0af10bf092..fa93acdf1de2 100644 --- a/test/protocol-tests/pom.xml +++ b/test/protocol-tests/pom.xml @@ -292,8 +292,8 @@ org.bouncycastle - bcpkix-jdk15on - 1.70 + bcpkix-jdk18on + 1.80 test