From 2f6525f68ab0fb6027d2b39f55f24ef3cf558cd8 Mon Sep 17 00:00:00 2001 From: hdavidh Date: Tue, 24 Jun 2025 11:28:47 -0700 Subject: [PATCH 1/7] Bump Netty to 4.2.2 --- bom-internal/pom.xml | 2 +- bundle-sdk/pom.xml | 2 +- http-clients/netty-nio-client/pom.xml | 2 +- .../awssdk/http/nio/netty/SdkEventLoopGroup.java | 5 +++-- .../nio/netty/internal/utils/ChannelResolver.java | 11 +++++++++-- .../http/nio/netty/internal/utils/NettyUtils.java | 1 - .../fault/ServerConnectivityErrorMessageTest.java | 6 ++++-- pom.xml | 4 ++-- test/http-client-tests/pom.xml | 4 ++-- test/protocol-tests/pom.xml | 4 ++-- 10 files changed, 25 insertions(+), 16 deletions(-) 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..cedbb2b360c6 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,8 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelFactory; import io.netty.channel.EventLoopGroup; -import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.MultiThreadIoEventLoopGroup; +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; @@ -142,7 +143,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..f517b86f179a 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; @@ -67,7 +68,7 @@ public static ChannelFactory resolveSocketChannelFactory(Even return resolveSocketChannelFactory(((DelegatingEventLoopGroup) eventLoopGroup).getDelegate()); } - if (eventLoopGroup instanceof NioEventLoopGroup) { + if (isNioGroup(eventLoopGroup)) { return NioSocketChannel::new; } if (eventLoopGroup instanceof EpollEventLoopGroup) { @@ -95,7 +96,7 @@ public static ChannelFactory resolveDatagramChannelFa return resolveDatagramChannelFactory(((DelegatingEventLoopGroup) eventLoopGroup).getDelegate()); } - if (eventLoopGroup instanceof NioEventLoopGroup) { + if (isNioGroup(eventLoopGroup)) { return NioDatagramChannel::new; } if (eventLoopGroup instanceof EpollEventLoopGroup) { @@ -109,4 +110,10 @@ public static ChannelFactory resolveDatagramChannelFa return invokeSafely(() -> new ReflectiveChannelFactory(Class.forName(datagramFqcn))); } + + private static boolean isNioGroup(EventLoopGroup group) { + return group instanceof NioEventLoopGroup || + group instanceof MultiThreadIoEventLoopGroup; + } + } 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/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/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 From b2789d59607e3db5a583e570fd228a9e721deac4 Mon Sep 17 00:00:00 2001 From: hdavidh Date: Tue, 24 Jun 2025 15:27:54 -0700 Subject: [PATCH 2/7] Fix ChannelResolver check for MultiThreadIoEventLoopGroup --- .../http/nio/netty/SdkEventLoopGroup.java | 5 +++++ .../netty/internal/utils/ChannelResolver.java | 18 ++++++------------ .../internal/utils/ChannelResolverTest.java | 8 ++++++++ 3 files changed, 19 insertions(+), 12 deletions(-) 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 cedbb2b360c6..4e6c8dedb461 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,6 +18,7 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelFactory; import io.netty.channel.EventLoopGroup; +import io.netty.channel.IoHandlerFactory; import io.netty.channel.MultiThreadIoEventLoopGroup; import io.netty.channel.nio.NioIoHandler; import io.netty.channel.socket.DatagramChannel; @@ -125,6 +126,10 @@ 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. + *

+ * If {@link MultiThreadIoEventLoopGroup} is passed in, {@link NioSocketChannel} and {@link NioDatagramChannel} will be + * resolved, regardless of the transport {@link IoHandlerFactory} passed in. This is because it is not possible to + * determine the type of transport factory from a given {@link MultiThreadIoEventLoopGroup}. * * @param eventLoopGroup the EventLoopGroup to be used * @return a new instance of SdkEventLoopGroup 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 f517b86f179a..a91eca72df6b 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 @@ -68,12 +68,12 @@ public static ChannelFactory resolveSocketChannelFactory(Even return resolveSocketChannelFactory(((DelegatingEventLoopGroup) eventLoopGroup).getDelegate()); } - if (isNioGroup(eventLoopGroup)) { - return NioSocketChannel::new; - } if (eventLoopGroup instanceof EpollEventLoopGroup) { return EpollSocketChannel::new; } + if (eventLoopGroup instanceof MultiThreadIoEventLoopGroup) { + return NioSocketChannel::new; + } String socketFqcn = KNOWN_EL_GROUPS_SOCKET_CHANNELS.get(eventLoopGroup.getClass().getName()); if (socketFqcn == null) { @@ -96,12 +96,12 @@ public static ChannelFactory resolveDatagramChannelFa return resolveDatagramChannelFactory(((DelegatingEventLoopGroup) eventLoopGroup).getDelegate()); } - if (isNioGroup(eventLoopGroup)) { - return NioDatagramChannel::new; - } if (eventLoopGroup instanceof EpollEventLoopGroup) { return EpollDatagramChannel::new; } + if (eventLoopGroup instanceof MultiThreadIoEventLoopGroup) { + return NioDatagramChannel::new; + } String datagramFqcn = KNOWN_EL_GROUPS_DATAGRAM_CHANNELS.get(eventLoopGroup.getClass().getName()); if (datagramFqcn == null) { @@ -110,10 +110,4 @@ public static ChannelFactory resolveDatagramChannelFa return invokeSafely(() -> new ReflectiveChannelFactory(Class.forName(datagramFqcn))); } - - private static boolean isNioGroup(EventLoopGroup group) { - return group instanceof NioEventLoopGroup || - group instanceof MultiThreadIoEventLoopGroup; - } - } 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()); From fb7ee3738e2de5aa66a32a0d6535b3abd3654acd Mon Sep 17 00:00:00 2001 From: hdavidh Date: Tue, 24 Jun 2025 15:29:28 -0700 Subject: [PATCH 3/7] Remove unused import --- .../awssdk/http/nio/netty/internal/utils/ChannelResolver.java | 1 - 1 file changed, 1 deletion(-) 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 a91eca72df6b..45b211738271 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 @@ -25,7 +25,6 @@ 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.socket.DatagramChannel; import io.netty.channel.socket.nio.NioDatagramChannel; import io.netty.channel.socket.nio.NioSocketChannel; From be0e2bfda86b55495c02539075df41dbbb4d4cd3 Mon Sep 17 00:00:00 2001 From: hdavidh Date: Tue, 24 Jun 2025 17:23:01 -0700 Subject: [PATCH 4/7] Add constructor for SdkEventLoopGroup --- .../http/nio/netty/SdkEventLoopGroup.java | 45 +++++++++++++++--- .../netty/internal/utils/ChannelResolver.java | 37 ++++++++++----- .../http/nio/netty/SdkEventLoopGroupTest.java | 47 +++++++++++++++++-- 3 files changed, 106 insertions(+), 23 deletions(-) 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 4e6c8dedb461..987fc2ddb307 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 @@ -34,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. * *

    *
  • using {@link #builder()} to provide custom configuration of {@link EventLoopGroup}. @@ -49,6 +49,9 @@ * *
  • Using {@link #create(EventLoopGroup, ChannelFactory)} to provide a custom {@link EventLoopGroup} and * {@link ChannelFactory} + * + *
  • Using {@link #create(EventLoopGroup, ChannelFactory, ChannelFactory)} to provide a custom {@link EventLoopGroup}, and + * socket and datagram {@link ChannelFactory}'s. *
* *

@@ -78,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 */ @@ -109,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 channelFactory) { - return new SdkEventLoopGroup(eventLoopGroup, channelFactory); + 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 socketChannelFactory, + ChannelFactory datagramChannelFactory) { + return new SdkEventLoopGroup(eventLoopGroup, socketChannelFactory, datagramChannelFactory); } /** @@ -128,8 +156,11 @@ public static SdkEventLoopGroup create(EventLoopGroup eventLoopGroup, ChannelFac * be thrown for any unknown EventLoopGroup type. *

* If {@link MultiThreadIoEventLoopGroup} is passed in, {@link NioSocketChannel} and {@link NioDatagramChannel} will be - * resolved, regardless of the transport {@link IoHandlerFactory} passed in. This is because it is not possible to - * determine the type of transport factory from a given {@link MultiThreadIoEventLoopGroup}. + * resolved, regardless of the transport {@link IoHandlerFactory} set on the {@link MultiThreadIoEventLoopGroup}. This is + * because it is not possible to determine the type of transport factory from a given {@link MultiThreadIoEventLoopGroup}. + *

+ * To use a {@link MultiThreadIoEventLoopGroup} with a non-Nio transport factory, use + * {@link #create(EventLoopGroup, ChannelFactory, ChannelFactory)}, specifying the socket and datagram channels. * * @param eventLoopGroup the EventLoopGroup to be used * @return a new instance of SdkEventLoopGroup 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 45b211738271..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 @@ -25,6 +25,7 @@ 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.socket.DatagramChannel; import io.netty.channel.socket.nio.NioDatagramChannel; import io.netty.channel.socket.nio.NioSocketChannel; @@ -67,19 +68,25 @@ public static ChannelFactory resolveSocketChannelFactory(Even return resolveSocketChannelFactory(((DelegatingEventLoopGroup) eventLoopGroup).getDelegate()); } + if (eventLoopGroup instanceof NioEventLoopGroup) { + return NioSocketChannel::new; + } if (eventLoopGroup instanceof EpollEventLoopGroup) { return EpollSocketChannel::new; } - if (eventLoopGroup instanceof MultiThreadIoEventLoopGroup) { - return NioSocketChannel::new; - } 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))); + } + + // 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; } - return invokeSafely(() -> new ReflectiveChannelFactory(Class.forName(socketFqcn))); + throw new IllegalArgumentException("Unknown event loop group : " + eventLoopGroup.getClass()); } /** @@ -95,18 +102,24 @@ public static ChannelFactory resolveDatagramChannelFa return resolveDatagramChannelFactory(((DelegatingEventLoopGroup) eventLoopGroup).getDelegate()); } + if (eventLoopGroup instanceof NioEventLoopGroup) { + return NioDatagramChannel::new; + } if (eventLoopGroup instanceof EpollEventLoopGroup) { return EpollDatagramChannel::new; } - if (eventLoopGroup instanceof MultiThreadIoEventLoopGroup) { - return NioDatagramChannel::new; - } 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/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())); } } From 7d7f38ab48f1239cb963b2bf64f176710425dd5e Mon Sep 17 00:00:00 2001 From: hdavidh Date: Tue, 24 Jun 2025 17:27:11 -0700 Subject: [PATCH 5/7] Changelog --- .changes/next-release/feature-AWSSDKforJavav2-a75db63.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changes/next-release/feature-AWSSDKforJavav2-a75db63.json diff --git a/.changes/next-release/feature-AWSSDKforJavav2-a75db63.json b/.changes/next-release/feature-AWSSDKforJavav2-a75db63.json new file mode 100644 index 000000000000..6b26bc4d117a --- /dev/null +++ b/.changes/next-release/feature-AWSSDKforJavav2-a75db63.json @@ -0,0 +1,6 @@ +{ + "type": "feature", + "category": "AWS SDK for Java v2", + "contributor": "", + "description": "Bump Netty to 4.2.2" +} From 132fe96744aeaa961133997a41bae45493d8e5b0 Mon Sep 17 00:00:00 2001 From: hdavidh Date: Thu, 26 Jun 2025 11:20:39 -0700 Subject: [PATCH 6/7] Update changelog --- .changes/next-release/feature-AWSSDKforJavav2-a75db63.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changes/next-release/feature-AWSSDKforJavav2-a75db63.json b/.changes/next-release/feature-AWSSDKforJavav2-a75db63.json index 6b26bc4d117a..b557210ac9d4 100644 --- a/.changes/next-release/feature-AWSSDKforJavav2-a75db63.json +++ b/.changes/next-release/feature-AWSSDKforJavav2-a75db63.json @@ -1,6 +1,6 @@ { "type": "feature", - "category": "AWS SDK for Java v2", + "category": "Netty NIO HTTP Client", "contributor": "", "description": "Bump Netty to 4.2.2" } From 0990da3f1c91a8c86e772f91a66680d412e8db34 Mon Sep 17 00:00:00 2001 From: hdavidh Date: Thu, 26 Jun 2025 12:15:02 -0700 Subject: [PATCH 7/7] Update javadocs --- .../awssdk/http/nio/netty/SdkEventLoopGroup.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) 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 987fc2ddb307..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,8 +18,8 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelFactory; import io.netty.channel.EventLoopGroup; -import io.netty.channel.IoHandlerFactory; 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; @@ -154,13 +154,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. + * *

- * If {@link MultiThreadIoEventLoopGroup} is passed in, {@link NioSocketChannel} and {@link NioDatagramChannel} will be - * resolved, regardless of the transport {@link IoHandlerFactory} set on the {@link MultiThreadIoEventLoopGroup}. This is - * because it is not possible to determine the type of transport factory from a given {@link MultiThreadIoEventLoopGroup}. + * 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 a {@link MultiThreadIoEventLoopGroup} with a non-Nio transport factory, use - * {@link #create(EventLoopGroup, ChannelFactory, ChannelFactory)}, specifying the socket and datagram channels. + * 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