diff --git a/apache-qpid-protonj2/pom.xml b/apache-qpid-protonj2/pom.xml index 48ecd376..502b7b54 100644 --- a/apache-qpid-protonj2/pom.xml +++ b/apache-qpid-protonj2/pom.xml @@ -18,7 +18,7 @@ org.apache.qpid protonj2-parent - 1.0.0-M22-SNAPSHOT + 1.0.0-M23-SNAPSHOT 4.0.0 diff --git a/pom.xml b/pom.xml index af524737..8ddd17c5 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ org.apache.qpid protonj2-parent - 1.0.0-M22-SNAPSHOT + 1.0.0-M23-SNAPSHOT pom Qpid ProtonJ2 Parent @@ -48,7 +48,6 @@ 3.0 4.1.113.Final 0.0.25.Final - 5.0.0.Alpha5 2.0.66.Final @@ -64,15 +63,10 @@ linux-x86_64 osx-x86_64 - provided - linux-x86_64 - linux-x86_64 - osx-x86_64 - -Xmx2g -enableassertions ${jacoco-config} - 2024-07-15T21:17:57Z + 2024-09-17T16:16:12Z https://qpid.apache.org/proton @@ -170,44 +164,6 @@ ${netty.iouring.version} ${netty-transport-native-io-uring-classifier} - - - io.netty - netty5-buffer - ${netty5.version} - - - io.netty - netty5-common - ${netty5.version} - - - io.netty - netty5-handler - ${netty5.version} - - - io.netty - netty5-transport - ${netty5.version} - - - io.netty - netty5-codec-http - ${netty5.version} - - - io.netty - netty5-transport-native-epoll - ${netty5.version} - ${netty-transport-native-epoll-classifier} - - - io.netty - netty5-transport-native-kqueue - ${netty5.version} - ${netty-transport-native-kqueue-classifier} - io.netty diff --git a/protonj2-client-docs/pom.xml b/protonj2-client-docs/pom.xml index e38dd804..9292992b 100644 --- a/protonj2-client-docs/pom.xml +++ b/protonj2-client-docs/pom.xml @@ -18,7 +18,7 @@ org.apache.qpid protonj2-parent - 1.0.0-M22-SNAPSHOT + 1.0.0-M23-SNAPSHOT 4.0.0 diff --git a/protonj2-client-examples/pom.xml b/protonj2-client-examples/pom.xml index 43bdde47..c5487cc6 100644 --- a/protonj2-client-examples/pom.xml +++ b/protonj2-client-examples/pom.xml @@ -20,7 +20,7 @@ org.apache.qpid protonj2-parent - 1.0.0-M22-SNAPSHOT + 1.0.0-M23-SNAPSHOT protonj2-client-examples diff --git a/protonj2-client/pom.xml b/protonj2-client/pom.xml index 09f81b66..a3f90520 100644 --- a/protonj2-client/pom.xml +++ b/protonj2-client/pom.xml @@ -22,7 +22,7 @@ org.apache.qpid protonj2-parent - 1.0.0-M22-SNAPSHOT + 1.0.0-M23-SNAPSHOT protonj2-client @@ -83,44 +83,6 @@ ${netty-transport-native-kqueue-classifier} ${netty-scope} - - - io.netty - netty5-buffer - ${netty5-scope} - - - io.netty - netty5-common - ${netty5-scope} - - - io.netty - netty5-handler - ${netty5-scope} - - - io.netty - netty5-transport - ${netty5-scope} - - - io.netty - netty5-codec-http - ${netty5-scope} - - - io.netty - netty5-transport-native-epoll - ${netty-transport-native-epoll-classifier} - ${netty5-scope} - - - io.netty - netty5-transport-native-kqueue - ${netty5-transport-native-kqueue-classifier} - ${netty5-scope} - org.apache.qpid diff --git a/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/impl/ClientDeliveryState.java b/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/impl/ClientDeliveryState.java index 883aff69..8ef7dbd3 100644 --- a/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/impl/ClientDeliveryState.java +++ b/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/impl/ClientDeliveryState.java @@ -217,7 +217,7 @@ public ClientRejected(String condition, String description, Map @Override public Type getType() { - return Type.RELEASED; + return Type.REJECTED; } @Override @@ -240,7 +240,9 @@ public static class ClientModified extends ClientDeliveryState { ClientModified(Modified modified) { this.modified.setDeliveryFailed(modified.isDeliveryFailed()); this.modified.setUndeliverableHere(modified.isUndeliverableHere()); - this.modified.setMessageAnnotations(new LinkedHashMap<>(modified.getMessageAnnotations())); + if (modified.getMessageAnnotations() != null) { + this.modified.setMessageAnnotations(new LinkedHashMap<>(modified.getMessageAnnotations())); + } } /** diff --git a/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/transport/IOContext.java b/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/transport/IOContext.java index 22c9e52a..c4f725a9 100644 --- a/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/transport/IOContext.java +++ b/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/transport/IOContext.java @@ -21,8 +21,6 @@ import org.apache.qpid.protonj2.client.TransportOptions; import org.apache.qpid.protonj2.client.transport.netty4.Netty4IOContext; import org.apache.qpid.protonj2.client.transport.netty4.Netty4Support; -import org.apache.qpid.protonj2.client.transport.netty5.Netty5IOContext; -import org.apache.qpid.protonj2.client.transport.netty5.Netty5Support; import org.apache.qpid.protonj2.engine.Scheduler; /** @@ -69,8 +67,6 @@ public interface IOContext { static IOContext create(TransportOptions options, SslOptions sslOptions, String ioThreadName) { if (Netty4Support.isAvailable()) { return new Netty4IOContext(options, sslOptions, ioThreadName); - } else if (Netty5Support.isAvailable()) { - return new Netty5IOContext(options, sslOptions, ioThreadName); } throw new UnsupportedOperationException("Netty not available on the class path"); diff --git a/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/transport/netty5/EpollSupport.java b/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/transport/netty5/EpollSupport.java deleted file mode 100644 index 2b774d92..00000000 --- a/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/transport/netty5/EpollSupport.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.qpid.protonj2.client.transport.netty5; - -import java.util.concurrent.ThreadFactory; - -import org.apache.qpid.protonj2.client.TransportOptions; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.netty5.channel.Channel; -import io.netty5.channel.EventLoopGroup; -import io.netty5.channel.MultithreadEventLoopGroup; -import io.netty5.channel.epoll.Epoll; -import io.netty5.channel.epoll.EpollHandler; -import io.netty5.channel.epoll.EpollSocketChannel; - -public final class EpollSupport { - - private static final Logger LOG = LoggerFactory.getLogger(EpollSupport.class); - - public static final String NAME = "EPOLL"; - - public static boolean isAvailable(TransportOptions transportOptions) { - try { - return transportOptions.allowNativeIO() && Epoll.isAvailable(); - } catch (NoClassDefFoundError ncdfe) { - LOG.debug("Unable to check for Epoll support due to missing class definition", ncdfe); - return false; - } - } - - public static EventLoopGroup createGroup(int nThreads, ThreadFactory ioThreadFactory) { - return new MultithreadEventLoopGroup(nThreads, ioThreadFactory, EpollHandler.newFactory()); - } - - public static Class getChannelClass() { - return EpollSocketChannel.class; - } -} diff --git a/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/transport/netty5/IOUringSupport.java b/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/transport/netty5/IOUringSupport.java deleted file mode 100644 index 7a435e14..00000000 --- a/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/transport/netty5/IOUringSupport.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.qpid.protonj2.client.transport.netty5; - -import java.util.concurrent.ThreadFactory; - -import org.apache.qpid.protonj2.client.TransportOptions; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.netty5.channel.Channel; -import io.netty5.channel.EventLoopGroup; - -public class IOUringSupport { - - private static final Logger LOG = LoggerFactory.getLogger(IOUringSupport.class); - - public static final String NAME = "IO_URING"; - - public static boolean isAvailable(TransportOptions transportOptions) { - try { - return false; //transportOptions.allowNativeIO() && IOUring.isAvailable(); - } catch (NoClassDefFoundError ncdfe) { - LOG.debug("Unable to check for IO_Uring support due to missing class definition", ncdfe); - return false; - } - } - - public static EventLoopGroup createGroup(int nThreads, ThreadFactory ioThreadFactory) { - // return new MultithreadEventLoopGroup(nThreads, ioThreadFactory, null); - throw new UnsupportedOperationException("Netty v5 doesn't yet support IO_Uring"); - } - - public static Class getChannelClass() { - // return IOUringSocketChannel.class; - throw new UnsupportedOperationException("Netty v5 doesn't yet support IO_Uring"); - } -} diff --git a/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/transport/netty5/KQueueSupport.java b/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/transport/netty5/KQueueSupport.java deleted file mode 100644 index 7c4fd76e..00000000 --- a/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/transport/netty5/KQueueSupport.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.qpid.protonj2.client.transport.netty5; - -import java.util.concurrent.ThreadFactory; - -import org.apache.qpid.protonj2.client.TransportOptions; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.netty5.channel.Channel; -import io.netty5.channel.EventLoopGroup; -import io.netty5.channel.MultithreadEventLoopGroup; -import io.netty5.channel.kqueue.KQueue; -import io.netty5.channel.kqueue.KQueueHandler; -import io.netty5.channel.kqueue.KQueueSocketChannel; - -public final class KQueueSupport { - - private static final Logger LOG = LoggerFactory.getLogger(KQueueSupport.class); - - public static final String NAME = "KQUEUE"; - - public static boolean isAvailable(TransportOptions transportOptions) { - try { - return transportOptions.allowNativeIO() && KQueue.isAvailable(); - } catch (NoClassDefFoundError ncdfe) { - LOG.debug("Unable to check for KQueue support due to missing class definition", ncdfe); - return false; - } - } - - public static EventLoopGroup createGroup(int nThreads, ThreadFactory ioThreadFactory) { - return new MultithreadEventLoopGroup(nThreads, ioThreadFactory, KQueueHandler.newFactory()); - } - - public static Class getChannelClass() { - return KQueueSocketChannel.class; - } -} diff --git a/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/transport/netty5/Netty5IOContext.java b/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/transport/netty5/Netty5IOContext.java deleted file mode 100644 index cffa2187..00000000 --- a/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/transport/netty5/Netty5IOContext.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.qpid.protonj2.client.transport.netty5; - -import java.util.Objects; -import java.util.concurrent.Callable; -import java.util.concurrent.Executor; -import java.util.concurrent.Future; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.TimeUnit; - -import org.apache.qpid.protonj2.client.SslOptions; -import org.apache.qpid.protonj2.client.TransportOptions; -import org.apache.qpid.protonj2.client.transport.IOContext; -import org.apache.qpid.protonj2.client.util.TrackableThreadFactory; -import org.apache.qpid.protonj2.engine.Scheduler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.netty5.bootstrap.Bootstrap; -import io.netty5.channel.Channel; -import io.netty5.channel.EventLoopGroup; -import io.netty5.channel.MultithreadEventLoopGroup; -import io.netty5.channel.nio.NioHandler; -import io.netty5.channel.socket.nio.NioSocketChannel; - -/** - * Builder of Transport instances that will validate the build options and produce a - * correctly configured transport based on the options set. - */ -public final class Netty5IOContext implements IOContext { - - private static final Logger LOG = LoggerFactory.getLogger(Netty5IOContext.class); - - private static final int SHUTDOWN_TIMEOUT = 50; - private static final int ASYNC_SHUTDOWN_TIMEOUT = 100; - private static final int ASYNC_SHUTDOWN_QUIET_PERIOD = 10; - - private final EventLoopGroup group; - private final NettyIOScheduler scheduler = new NettyIOScheduler(); - private final Class channelClass; - private final TransportOptions options; - private final SslOptions sslOptions; - private final ThreadFactory threadFactory; - - public Netty5IOContext(TransportOptions options, SslOptions ssl, String ioThreadName) { - Objects.requireNonNull(options, "Transport Options cannot be null"); - Objects.requireNonNull(ssl, "Transport SSL Options cannot be null"); - - this.options = options; - this.sslOptions = ssl; - this.threadFactory = new TrackableThreadFactory(ioThreadName, true); - - final String[] nativeIOPreference = options.nativeIOPreference(); - - EventLoopGroup selectedGroup = null; - Class selectedChannelClass = null; - - if (options.allowNativeIO()) { - for (String nativeID : nativeIOPreference) { - if (EpollSupport.NAME.equalsIgnoreCase(nativeID)) { - if (EpollSupport.isAvailable(options)) { - LOG.trace("Netty Transports will be using Epoll mode"); - selectedGroup = EpollSupport.createGroup(1, threadFactory); - selectedChannelClass = EpollSupport.getChannelClass(); - break; - } - } else if (IOUringSupport.NAME.equalsIgnoreCase(nativeID)) { - if (IOUringSupport.isAvailable(options)) { - LOG.trace("Netty Transports will be using IO-Uring mode"); - selectedGroup = IOUringSupport.createGroup(1, threadFactory); - selectedChannelClass = IOUringSupport.getChannelClass(); - break; - } - } else if (KQueueSupport.NAME.equalsIgnoreCase(nativeID)) { - if (KQueueSupport.isAvailable(options)) { - LOG.trace("Netty Transports will be using KQueue mode"); - selectedGroup = KQueueSupport.createGroup(1, threadFactory); - selectedChannelClass = KQueueSupport.getChannelClass(); - break; - } - } else { - throw new IllegalArgumentException( - String.format("Provided preferred native transport type name: %s, is not supported.", nativeID)); - } - } - } - - if (selectedGroup == null) { - LOG.trace("Netty Transports will be using NIO mode"); - selectedGroup = new MultithreadEventLoopGroup(1, threadFactory, NioHandler.newFactory()); - selectedChannelClass = NioSocketChannel.class; - } - - this.group = selectedGroup; - this.channelClass = selectedChannelClass; - } - - @Override - public void shutdown() { - if (!group.isShutdown()) { - group.shutdownGracefully(0, SHUTDOWN_TIMEOUT, TimeUnit.MILLISECONDS); - try { - if (!group.awaitTermination(2 * SHUTDOWN_TIMEOUT, TimeUnit.MILLISECONDS)) { - LOG.trace("Connection IO Event Loop shutdown failed to complete in allotted time"); - } - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } - - /** - * Shutdown the event loop asynchronously with a grace period for work that might be in-bound - * at the time of termination. This is safe to call from inside the event loop where the - * standard blocking shutdown API is not. - */ - @Override - public void shutdownAsync() { - if (!group.isShutdown()) { - group.shutdownGracefully(ASYNC_SHUTDOWN_QUIET_PERIOD, ASYNC_SHUTDOWN_TIMEOUT, TimeUnit.MILLISECONDS); - } - } - - @Override - public Scheduler ioScheduler() { - return scheduler; - } - - @Override - public TcpTransport newTransport() { - if (group.isShutdown() || group.isShuttingDown() || group.isTerminated()) { - throw new IllegalStateException("Cannot create a Transport from a shutdown IO context"); - } - - final Bootstrap bootstrap = new Bootstrap().channel(channelClass).group(group); - - final TcpTransport transport; - - if (options.useWebSockets()) { - transport = new WebSocketTransport(bootstrap, options, sslOptions); - } else { - transport = new TcpTransport(bootstrap, options, sslOptions); - } - - return transport; - } - - public class NettyIOScheduler implements Scheduler, Executor { - - @Override - public void execute(Runnable command) { - group.execute(command); - } - - @Override - public Future schedule(Runnable command, long delay, TimeUnit unit) { - return group.schedule(command, delay, unit).asStage(); - } - - @Override - public Future schedule(Callable task, long delay, TimeUnit unit) { - return group.schedule(task, delay, unit).asStage(); - } - - @Override - public Future scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { - return group.scheduleAtFixedRate(command, initialDelay, period, unit).asStage(); - } - - @Override - public Future scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { - return group.scheduleWithFixedDelay(command, initialDelay, delay, unit).asStage(); - } - - @Override - public boolean isShutdown() { - return group.isShutdown(); - } - } -} diff --git a/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/transport/netty5/Netty5Support.java b/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/transport/netty5/Netty5Support.java deleted file mode 100644 index 1bb8ac87..00000000 --- a/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/transport/netty5/Netty5Support.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.qpid.protonj2.client.transport.netty5; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.netty5.buffer.BufferAllocator; - -/** - * Support class used to detect if Netty 5 is available on the class path. - */ -public final class Netty5Support { - - private static final Logger LOG = LoggerFactory.getLogger(Netty5Support.class); - - private static final Throwable UNAVAILABILITY_CAUSE; - static { - Throwable cause = null; - try { - BufferAllocator.onHeapUnpooled(); - } catch (Throwable ex) { - LOG.debug("Netty 5 not available for use."); - cause = ex; - } - - UNAVAILABILITY_CAUSE = cause; - } - - public static final boolean isAvailable() { - return UNAVAILABILITY_CAUSE == null; - } -} diff --git a/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/transport/netty5/SslSupport.java b/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/transport/netty5/SslSupport.java deleted file mode 100644 index 4d1ffe3c..00000000 --- a/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/transport/netty5/SslSupport.java +++ /dev/null @@ -1,474 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.qpid.protonj2.client.transport.netty5; - -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLParameters; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509ExtendedKeyManager; - -import org.apache.qpid.protonj2.client.SslOptions; -import org.apache.qpid.protonj2.client.transport.X509AliasKeyManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.netty5.buffer.BufferAllocator; -import io.netty5.handler.ssl.OpenSsl; -import io.netty5.handler.ssl.OpenSslX509KeyManagerFactory; -import io.netty5.handler.ssl.SslContext; -import io.netty5.handler.ssl.SslContextBuilder; -import io.netty5.handler.ssl.SslHandler; -import io.netty5.handler.ssl.SslProvider; -import io.netty5.handler.ssl.util.InsecureTrustManagerFactory; - -/** - * Static class that provides various utility methods used by Transport implementations. - */ -public final class SslSupport { - - private static final Logger LOG = LoggerFactory.getLogger(SslSupport.class); - - public static final String FROM_CLASSPATH_PREFIX = "classpath:"; - public static final String FROM_FILE_PREFIX = "file:"; - public static final String FROM_FILE_URL_PREFIX = "file://"; - - /** - * Determines if Netty OpenSSL support is available and applicable based on the configuration - * in the given TransportOptions instance. - * - * @param options - * The configuration of the Transport being created. - * - * @return true if OpenSSL support is available and usable given the requested configuration. - */ - public static boolean isOpenSSLPossible(SslOptions options) { - boolean result = false; - - if (options.allowNativeSSL()) { - if (!OpenSsl.isAvailable()) { - LOG.debug("OpenSSL could not be enabled because a suitable implementation could not be found.", OpenSsl.unavailabilityCause()); - } else if (options.sslContextOverride() != null) { - LOG.debug("OpenSSL could not be enabled due to user SSLContext being supplied."); - } else if (!OpenSsl.supportsKeyManagerFactory()) { - LOG.debug("OpenSSL could not be enabled because the version provided does not allow a KeyManagerFactory to be used."); - } else if (options.keyAlias() != null) { - LOG.debug("OpenSSL could not be enabled because a keyAlias is set and that feature is not supported for OpenSSL."); - } else { - LOG.debug("OpenSSL Enabled: Version {} of OpenSSL will be used", OpenSsl.versionString()); - result = true; - } - } - - return result; - } - - /** - * Creates a Netty SslHandler instance for use in Transports that require - * an SSL encoder / decoder. - * - * If the given options contain an SSLContext override, this will be used directly - * when creating the handler. If they do not, an SSLContext will first be created - * using the other option values. - * - * @param allocator - * The Netty Buffer Allocator to use when Netty resources need to be created. - * @param host - * the host name or IP address that this transport connects to. - * @param port - * the port on the given host that this transport connects to. - * @param options - * The SSL options object to build the SslHandler instance from. - * - * @return a new SslHandler that is configured from the given options. - * - * @throws Exception if an error occurs while creating the SslHandler instance. - */ - public static SslHandler createSslHandler(BufferAllocator allocator, String host, int port, SslOptions options) throws Exception { - final SSLEngine sslEngine; - - if (isOpenSSLPossible(options)) { - SslContext sslContext = createOpenSslContext(options); - sslEngine = createOpenSslEngine(allocator, host, port, sslContext, options); - } else { - SSLContext sslContext = options.sslContextOverride(); - if (sslContext == null) { - sslContext = createJdkSslContext(options); - } - - sslEngine = createJdkSslEngine(host, port, sslContext, options); - } - - return new SslHandler(sslEngine); - } - - //----- JDK SSL Support Methods ------------------------------------------// - - /** - * Create a new SSLContext using the options specific in the given TransportOptions - * instance. - * - * @param options - * the configured options used to create the SSLContext. - * - * @return a new SSLContext instance. - * - * @throws Exception if an error occurs while creating the context. - */ - public static SSLContext createJdkSslContext(SslOptions options) throws Exception { - try { - String contextProtocol = options.contextProtocol(); - LOG.trace("Getting SSLContext instance using protocol: {}", contextProtocol); - - SSLContext context = SSLContext.getInstance(contextProtocol); - - KeyManager[] keyMgrs = loadKeyManagers(options); - TrustManager[] trustManagers = loadTrustManagers(options); - - context.init(keyMgrs, trustManagers, new SecureRandom()); - return context; - } catch (Exception e) { - LOG.error("Failed to create SSLContext: {}", e, e); - throw e; - } - } - - /** - * Create a new JDK SSLEngine instance in client mode from the given SSLContext and - * TransportOptions instances. - * - * @param host - * the host name or IP address that this transport connects to. - * @param port - * the port on the given host that this transport connects to. - * @param context - * the SSLContext to use when creating the engine. - * @param options - * the TransportOptions to use to configure the new SSLEngine. - * - * @return a new SSLEngine instance in client mode. - * - * @throws Exception if an error occurs while creating the new SSLEngine. - */ - public static SSLEngine createJdkSslEngine(String host, int port, SSLContext context, SslOptions options) throws Exception { - SSLEngine engine = null; - if (host == null || host.isEmpty()) { - engine = context.createSSLEngine(); - } else { - engine = context.createSSLEngine(host, port); - } - - engine.setEnabledProtocols(buildEnabledProtocols(engine, options)); - engine.setEnabledCipherSuites(buildEnabledCipherSuites(engine, options)); - engine.setUseClientMode(true); - - if (options.verifyHost()) { - SSLParameters sslParameters = engine.getSSLParameters(); - sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); - engine.setSSLParameters(sslParameters); - } - - return engine; - } - - //----- OpenSSL Support Methods ------------------------------------------// - - /** - * Create a new Netty SslContext using the options specific in the given TransportOptions - * instance. - * - * @param options - * the configured options used to create the SslContext. - * - * @return a new SslContext instance. - * - * @throws Exception if an error occurs while creating the context. - */ - public static SslContext createOpenSslContext(SslOptions options) throws Exception { - try { - String contextProtocol = options.contextProtocol(); - LOG.trace("Getting SslContext instance using protocol: {}", contextProtocol); - - KeyManagerFactory keyManagerFactory = loadKeyManagerFactory(options, SslProvider.OPENSSL); - TrustManagerFactory trustManagerFactory = loadTrustManagerFactory(options); - SslContextBuilder builder = SslContextBuilder.forClient().sslProvider(SslProvider.OPENSSL); - - // TODO - There is oddly no way in Netty right now to get the set of supported protocols - // when creating the SslContext or really even when creating the SSLEngine. Seems - // like an oversight, for now we call it with TLSv1.2 so it looks like we did something. - if (options.contextProtocol().equals(SslOptions.DEFAULT_CONTEXT_PROTOCOL)) { - builder.protocols("TLSv1.2"); - } else { - builder.protocols(options.contextProtocol()); - } - builder.keyManager(keyManagerFactory); - builder.trustManager(trustManagerFactory); - - return builder.build(); - } catch (Exception e) { - LOG.error("Failed to create SslContext: {}", e, e); - throw e; - } - } - - /** - * Create a new OpenSSL SSLEngine instance in client mode from the given SSLContext and - * TransportOptions instances. - * - * @param allocator - * the Netty BufferAllocator to use to create the OpenSSL engine - * @param host - * the host name or IP address that this transport connects to. - * @param port - * the port on the given host that this transport connects to. - * @param context - * the Netty SslContext to use when creating the engine. - * @param options - * the TransportOptions to use to configure the new SSLEngine. - * - * @return a new Netty managed SSLEngine instance in client mode. - * - * @throws Exception if an error occurs while creating the new SSLEngine. - */ - public static SSLEngine createOpenSslEngine(BufferAllocator allocator, String host, int port, SslContext context, SslOptions options) throws Exception { - SSLEngine engine = null; - - if (allocator == null) { - throw new IllegalArgumentException("OpenSSL engine requires a valid ByteBufAllocator to operate"); - } - - if (host == null || host.isEmpty()) { - engine = context.newEngine(allocator); - } else { - engine = context.newEngine(allocator, host, port); - } - - engine.setEnabledProtocols(buildEnabledProtocols(engine, options)); - engine.setEnabledCipherSuites(buildEnabledCipherSuites(engine, options)); - engine.setUseClientMode(true); - - if (options.verifyHost()) { - SSLParameters sslParameters = engine.getSSLParameters(); - sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); - engine.setSSLParameters(sslParameters); - } - - return engine; - } - - //----- Internal support methods -----------------------------------------// - - private static String[] buildEnabledProtocols(SSLEngine engine, SslOptions options) { - List enabledProtocols = new ArrayList(); - - if (options.enabledProtocols() != null) { - List configuredProtocols = Arrays.asList(options.enabledProtocols()); - LOG.trace("Configured protocols from transport options: {}", configuredProtocols); - enabledProtocols.addAll(configuredProtocols); - } else { - List engineProtocols = Arrays.asList(engine.getEnabledProtocols()); - LOG.trace("Default protocols from the SSLEngine: {}", engineProtocols); - enabledProtocols.addAll(engineProtocols); - } - - String[] disabledProtocols = options.disabledProtocols(); - if (disabledProtocols != null) { - List disabled = Arrays.asList(disabledProtocols); - LOG.trace("Disabled protocols: {}", disabled); - enabledProtocols.removeAll(disabled); - } - - LOG.trace("Enabled protocols: {}", enabledProtocols); - - return enabledProtocols.toArray(new String[0]); - } - - private static String[] buildEnabledCipherSuites(SSLEngine engine, SslOptions options) { - List enabledCipherSuites = new ArrayList(); - - if (options.enabledCipherSuites() != null) { - List configuredCipherSuites = Arrays.asList(options.enabledCipherSuites()); - LOG.trace("Configured cipher suites from transport options: {}", configuredCipherSuites); - enabledCipherSuites.addAll(configuredCipherSuites); - } else { - List engineCipherSuites = Arrays.asList(engine.getEnabledCipherSuites()); - LOG.trace("Default cipher suites from the SSLEngine: {}", engineCipherSuites); - enabledCipherSuites.addAll(engineCipherSuites); - } - - String[] disabledCipherSuites = options.disabledCipherSuites(); - if (disabledCipherSuites != null) { - List disabled = Arrays.asList(disabledCipherSuites); - LOG.trace("Disabled cipher suites: {}", disabled); - enabledCipherSuites.removeAll(disabled); - } - - LOG.trace("Enabled cipher suites: {}", enabledCipherSuites); - - return enabledCipherSuites.toArray(new String[0]); - } - - private static TrustManager[] loadTrustManagers(SslOptions options) throws Exception { - TrustManagerFactory factory = loadTrustManagerFactory(options); - if (factory != null) { - return factory.getTrustManagers(); - } else { - return null; - } - } - - private static TrustManagerFactory loadTrustManagerFactory(SslOptions options) throws Exception { - if (options.trustAll()) { - return InsecureTrustManagerFactory.INSTANCE; - } - - if (options.trustStoreLocation() == null) { - return null; - } - - TrustManagerFactory fact = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - - String storeLocation = options.trustStoreLocation(); - String storePassword = options.trustStorePassword(); - String storeType = options.trustStoreType(); - - LOG.trace("Attempt to load TrustStore from location {} of type {}", storeLocation, storeType); - - KeyStore trustStore = loadStore(storeLocation, storePassword, storeType); - fact.init(trustStore); - - return fact; - } - - private static KeyManager[] loadKeyManagers(SslOptions options) throws Exception { - if (options.keyStoreLocation() == null) { - return null; - } - - KeyManagerFactory fact = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - - String storeLocation = options.keyStoreLocation(); - String storePassword = options.keyStorePassword(); - String storeType = options.keyStoreType(); - String alias = options.keyAlias(); - - LOG.trace("Attempt to load KeyStore from location {} of type {}", storeLocation, storeType); - - KeyStore keyStore = loadStore(storeLocation, storePassword, storeType); - fact.init(keyStore, storePassword != null ? storePassword.toCharArray() : null); - - if (alias == null) { - return fact.getKeyManagers(); - } else { - validateAlias(keyStore, alias); - return wrapKeyManagers(alias, fact.getKeyManagers()); - } - } - - private static KeyManagerFactory loadKeyManagerFactory(SslOptions options, SslProvider provider) throws Exception { - if (options.keyStoreLocation() == null) { - return null; - } - - final KeyManagerFactory factory; - if (provider.equals(SslProvider.JDK)) { - factory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - } else { - factory = new OpenSslX509KeyManagerFactory(); - } - - String storeLocation = options.keyStoreLocation(); - String storePassword = options.keyStorePassword(); - String storeType = options.keyStoreType(); - - LOG.trace("Attempt to load KeyStore from location {} of type {}", storeLocation, storeType); - - KeyStore keyStore = loadStore(storeLocation, storePassword, storeType); - factory.init(keyStore, storePassword != null ? storePassword.toCharArray() : null); - - return factory; - } - - private static KeyManager[] wrapKeyManagers(String alias, KeyManager[] origKeyManagers) { - KeyManager[] keyManagers = new KeyManager[origKeyManagers.length]; - for (int i = 0; i < origKeyManagers.length; i++) { - KeyManager km = origKeyManagers[i]; - if (km instanceof X509ExtendedKeyManager) { - km = new X509AliasKeyManager(alias, (X509ExtendedKeyManager) km); - } - - keyManagers[i] = km; - } - - return keyManagers; - } - - private static void validateAlias(KeyStore store, String alias) throws IllegalArgumentException, KeyStoreException { - if (!store.containsAlias(alias)) { - throw new IllegalArgumentException("The alias '" + alias + "' doesn't exist in the key store"); - } - - if (!store.isKeyEntry(alias)) { - throw new IllegalArgumentException("The alias '" + alias + "' in the keystore doesn't represent a key entry"); - } - } - - private static KeyStore loadStore(String storePath, final String password, String storeType) throws Exception { - KeyStore store = KeyStore.getInstance(storeType); - try (InputStream in = openStoreAtLocation(storePath)) { - store.load(in, password != null ? password.toCharArray() : null); - } catch (Exception ex) { - LOG.trace("Caught Error loading store: {}", ex.getMessage(), ex); - throw ex; - } - - return store; - } - - private static InputStream openStoreAtLocation(final String storePath) throws IOException { - final InputStream stream; - - if (storePath.startsWith(FROM_CLASSPATH_PREFIX)) { - stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(storePath.substring(FROM_CLASSPATH_PREFIX.length())); - } else if (storePath.startsWith(FROM_FILE_URL_PREFIX)) { - stream = new FileInputStream(storePath.substring(FROM_FILE_URL_PREFIX.length())); - } else if (storePath.startsWith(FROM_FILE_PREFIX)) { - stream = new FileInputStream(storePath.substring(FROM_FILE_PREFIX.length())); - } else { - stream = new FileInputStream(storePath); - } - - if (stream == null) { - throw new IOException("Could no locate KeyStore at location: " + storePath); - } - - return stream; - } -} diff --git a/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/transport/netty5/TcpTransport.java b/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/transport/netty5/TcpTransport.java deleted file mode 100644 index 5fba50b6..00000000 --- a/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/transport/netty5/TcpTransport.java +++ /dev/null @@ -1,536 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.qpid.protonj2.client.transport.netty5; - -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.security.Principal; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicBoolean; - -import org.apache.qpid.protonj2.buffer.ProtonBuffer; -import org.apache.qpid.protonj2.buffer.ProtonBufferAllocator; -import org.apache.qpid.protonj2.buffer.ProtonBufferComponent; -import org.apache.qpid.protonj2.buffer.ProtonBufferComponentAccessor; -import org.apache.qpid.protonj2.buffer.netty.Netty5ProtonBufferAllocator; -import org.apache.qpid.protonj2.buffer.netty.Netty5ToProtonBufferAdapter; -import org.apache.qpid.protonj2.buffer.netty.ProtonBufferToNetty5Adapter; -import org.apache.qpid.protonj2.client.SslOptions; -import org.apache.qpid.protonj2.client.TransportOptions; -import org.apache.qpid.protonj2.client.transport.Transport; -import org.apache.qpid.protonj2.client.transport.TransportListener; -import org.apache.qpid.protonj2.client.util.IOExceptionSupport; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.netty5.bootstrap.Bootstrap; -import io.netty5.buffer.Buffer; -import io.netty5.channel.Channel; -import io.netty5.channel.ChannelHandler; -import io.netty5.channel.ChannelHandlerContext; -import io.netty5.channel.ChannelInitializer; -import io.netty5.channel.ChannelOption; -import io.netty5.channel.ChannelPipeline; -import io.netty5.channel.SimpleChannelInboundHandler; -import io.netty5.handler.logging.LoggingHandler; -import io.netty5.handler.ssl.SslHandler; -import io.netty5.util.concurrent.Future; -import io.netty5.util.concurrent.FutureListener; - -/** - * TCP based transport that uses Netty as the underlying IO layer. - */ -public class TcpTransport implements Transport { - - private static final Logger LOG = LoggerFactory.getLogger(TcpTransport.class); - - protected final AtomicBoolean connected = new AtomicBoolean(); - protected final AtomicBoolean closed = new AtomicBoolean(); - protected final CountDownLatch connectedLatch = new CountDownLatch(1); - protected final TransportOptions options; - protected final SslOptions sslOptions; - protected final Bootstrap bootstrap; - - protected Channel channel; - protected volatile IOException failureCause; - protected String host; - protected int port; - protected TransportListener listener; - protected Netty5ProtonBufferAllocator nettyAllocator; - - /** - * Create a new {@link TcpTransport} instance with the given configuration. - * - * @param bootstrap - * the Netty {@link Bootstrap} that this transport's IO layer is bound to. - * @param options - * the {@link TransportOptions} used to configure the socket connection. - * @param sslOptions - * the {@link SslOptions} to use if the options indicate SSL is enabled. - */ - public TcpTransport(Bootstrap bootstrap, TransportOptions options, SslOptions sslOptions) { - if (options == null) { - throw new IllegalArgumentException("Transport Options cannot be null"); - } - - if (sslOptions == null) { - throw new IllegalArgumentException("Transport SSL Options cannot be null"); - } - - if (bootstrap == null) { - throw new IllegalArgumentException("A transport must have an assigned Bootstrap before connect."); - } - - this.sslOptions = sslOptions; - this.options = options; - this.bootstrap = bootstrap; - } - - @Override - public TcpTransport connect(String host, int port, TransportListener listener) throws IOException { - if (closed.get()) { - throw new IllegalStateException("Transport has already been closed"); - } - - if (listener == null) { - throw new IllegalArgumentException("A transport listener must be set before connection attempts."); - } - - if (host == null || host.isEmpty()) { - throw new IllegalArgumentException("Transport host value cannot be null"); - } - - if (port < 0 && options.defaultTcpPort() < 0 && (sslOptions.sslEnabled() && sslOptions.defaultSslPort() < 0)) { - throw new IllegalArgumentException("Transport port value must be a non-negative int value or a default port configured"); - } - - this.host = host; - this.listener = listener; - - if (port > 0) { - this.port = port; - } else { - if (sslOptions.sslEnabled()) { - this.port = sslOptions.defaultSslPort(); - } else { - this.port = options.defaultTcpPort(); - } - } - - bootstrap.handler(new ChannelInitializer<>() { - @Override - public void initChannel(Channel transportChannel) throws Exception { - channel = transportChannel; - nettyAllocator = new Netty5ProtonBufferAllocator(channel.bufferAllocator()); - - configureChannel(transportChannel); - try { - listener.transportInitialized(TcpTransport.this); - } catch (Throwable initError) { - LOG.warn("Error during initialization of channel from Transport Listener"); - handleTransportFailure(transportChannel, IOExceptionSupport.create(initError)); - throw initError; - } - } - }); - - configureNetty(bootstrap, options); - - bootstrap.connect(getHost(), getPort()).addListener(new FutureListener() { - - @Override - public void operationComplete(Future future) throws Exception { - if (future.isFailed()) { - bootstrap.config().group().execute(() -> handleTransportFailure(channel, future.cause())); - } - } - }); - - return this; - } - - @Override - public void awaitConnect() throws InterruptedException, IOException { - connectedLatch.await(); - if (!connected.get()) { - if (failureCause != null) { - throw failureCause; - } else { - throw new IOException("Transport was closed before a connection was established."); - } - } - } - - @Override - public boolean isConnected() { - return connected.get(); - } - - @Override - public boolean isSecure() { - return sslOptions.sslEnabled(); - } - - @Override - public String getHost() { - return host; - } - - @Override - public int getPort() { - return port; - } - - @Override - public void close() throws IOException { - if (closed.compareAndSet(false, true)) { - connected.set(false); - connectedLatch.countDown(); - if (channel != null) { - try { - channel.close().asStage().await(); - } catch (InterruptedException e) { - Thread.interrupted(); - } - } - } - } - - @Override - public ProtonBufferAllocator getBufferAllocator() { - return nettyAllocator; - } - - @Override - public TcpTransport write(ProtonBuffer output) throws IOException { - return write(output, null); - } - - @Override - public TcpTransport write(ProtonBuffer output, Runnable onComplete) throws IOException { - checkConnected(output); - LOG.trace("Attempted write of buffer: {}", output); - return writeOutputBuffer(output, false, onComplete); - } - - @Override - public TcpTransport writeAndFlush(ProtonBuffer output) throws IOException { - return writeAndFlush(output, null); - } - - @Override - public TcpTransport writeAndFlush(ProtonBuffer output, Runnable onComplete) throws IOException { - checkConnected(output); - LOG.trace("Attempted write and flush of buffer: {}", output); - return writeOutputBuffer(output, true, onComplete); - } - - @Override - public TcpTransport flush() throws IOException { - checkConnected(); - LOG.trace("Attempted flush of pending writes"); - channel.flush(); - return this; - } - - @Override - public TransportListener getTransportListener() { - return listener; - } - - @Override - public TransportOptions getTransportOptions() { - return options.clone(); - } - - @Override - public SslOptions getSslOptions() { - return sslOptions.clone(); - } - - @Override - public Principal getLocalPrincipal() { - Principal result = null; - - if (isSecure()) { - SslHandler sslHandler = channel.pipeline().get(SslHandler.class); - result = sslHandler.engine().getSession().getLocalPrincipal(); - } - - return result; - } - - private TcpTransport writeOutputBuffer(final ProtonBuffer buffer, boolean flush, Runnable onComplete) { - int writeCount = buffer.componentCount(); - Future writeFuture = null; - - try (ProtonBuffer ioBuffer = buffer; ProtonBufferComponentAccessor accessor = buffer.componentAccessor()) { - for (ProtonBufferComponent output = accessor.firstReadable(); output != null; output = accessor.nextReadable()) { - final Buffer nettyBuf; - - if (output instanceof Netty5ToProtonBufferAdapter) { - nettyBuf = ((Netty5ToProtonBufferAdapter)output).unwrapAndRelease(); - } else if (output.unwrap() instanceof Buffer) { - nettyBuf = ((Buffer) output.unwrap()).copy(true); - } else { - nettyBuf = channel.bufferAllocator().allocate(output.getReadableBytes()); - if (output.hasReadbleArray()) { - nettyBuf.writeBytes(output.getReadableArray(), output.getReadableArrayOffset(), output.getReadableBytes()); - } else { - nettyBuf.writeBytes(output.getReadableBuffer()); - } - } - - if (--writeCount == 0 && flush) { - writeFuture = channel.writeAndFlush(nettyBuf); - } else { - writeFuture = channel.write(nettyBuf); - } - } - - if (onComplete != null) { - writeFuture.addListener(onComplete, TcpTransport::handleWriteComplete); - } - } - - return this; - } - - @SuppressWarnings("unused") - private TcpTransport writeOutputBufferAsWrappedNettyBuffer(final ProtonBuffer buffer, boolean flush, Runnable onComplete) { - Future writeFuture = null; - - final Buffer nettyBuf = new ProtonBufferToNetty5Adapter(buffer.transfer()); - - if (flush) { - writeFuture = channel.writeAndFlush(nettyBuf); - } else { - writeFuture = channel.write(nettyBuf); - } - - if (onComplete != null) { - writeFuture.addListener(onComplete, TcpTransport::handleWriteComplete); - } - - return this; - } - - private static void handleWriteComplete(Runnable onComplete, Future future) { - if (future.isSuccess()) { - onComplete.run(); - } - } - - //----- Internal implementation details, can be overridden as needed -----// - - protected void addAdditionalHandlers(ChannelPipeline pipeline) { - - } - - protected ChannelHandler createChannelHandler() { - return new NettyTcpTransportHandler(); - } - - //----- Event Handlers which can be overridden in subclasses -------------// - - protected void handleConnected(Channel connectedChannel) throws Exception { - LOG.trace("Channel has become active! Channel is {}", connectedChannel); - channel = connectedChannel; - connected.set(true); - listener.transportConnected(this); - connectedLatch.countDown(); - } - - protected void handleTransportFailure(Channel failedChannel, Throwable cause) { - if (!closed.get()) { - LOG.trace("Transport indicates connection failure! Channel is {}", failedChannel); - failureCause = IOExceptionSupport.create(cause); - channel = failedChannel; - connected.set(false); - connectedLatch.countDown(); - - LOG.trace("Firing onTransportError listener"); - if (channel.executor().inEventLoop()) { - listener.transportError(failureCause); - } else { - channel.executor().execute(() -> { - listener.transportError(failureCause); - }); - } - } else { - LOG.trace("Closed Transport signalled that the channel ended: {}", channel); - } - } - - //----- State change handlers and checks ---------------------------------// - - protected final void checkConnected() throws IOException { - if (!connected.get() || !channel.isActive()) { - throw new IOException("Cannot send to a non-connected transport.", failureCause); - } - } - - private void checkConnected(ProtonBuffer output) throws IOException { - if (!connected.get() || !channel.isActive()) { - output.close(); - throw new IOException("Cannot send to a non-connected transport.", failureCause); - } - } - - private void configureNetty(Bootstrap bootstrap, TransportOptions options) { - bootstrap.option(ChannelOption.TCP_NODELAY, options.tcpNoDelay()); - bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, options.connectTimeout()); - bootstrap.option(ChannelOption.SO_KEEPALIVE, options.tcpKeepAlive()); - bootstrap.option(ChannelOption.SO_LINGER, options.soLinger()); - - if (options.sendBufferSize() != -1) { - bootstrap.option(ChannelOption.SO_SNDBUF, options.sendBufferSize()); - } - - if (options.receiveBufferSize() != -1) { - bootstrap.option(ChannelOption.SO_RCVBUF, options.receiveBufferSize()); - } - - if (options.trafficClass() != -1) { - bootstrap.option(ChannelOption.IP_TOS, options.trafficClass()); - } - - if (options.localAddress() != null || options.localPort() != 0) { - if (options.localAddress() != null) { - bootstrap.localAddress(options.localAddress(), options.localPort()); - } else { - bootstrap.localAddress(options.localPort()); - } - } - } - - private void configureChannel(final Channel channel) throws Exception { - if (isSecure()) { - final SslHandler sslHandler; - try { - sslHandler = SslSupport.createSslHandler(channel.bufferAllocator(), host, port, sslOptions); - } catch (Exception ex) { - LOG.warn("Error during initialization of channel from SSL Handler creation:"); - handleTransportFailure(channel, IOExceptionSupport.create(ex)); - throw IOExceptionSupport.create(ex); - } - - channel.pipeline().addLast("ssl", sslHandler); - } - - if (options.traceBytes()) { - channel.pipeline().addLast("logger", new LoggingHandler(getClass())); - } - - addAdditionalHandlers(channel.pipeline()); - - channel.pipeline().addLast(createChannelHandler()); - } - - //----- Default implementation of Netty handler --------------------------// - - protected abstract class NettyDefaultHandler extends SimpleChannelInboundHandler { - - public NettyDefaultHandler() { - super(false); // We will release buffer references manually. - } - - @Override - public final void channelRegistered(ChannelHandlerContext context) throws Exception { - channel = context.channel(); - } - - @Override - public void channelActive(ChannelHandlerContext context) throws Exception { - // In the Secure case we need to let the handshake complete before we - // trigger the connected event. - if (!isSecure()) { - handleConnected(context.channel()); - } else { - SslHandler sslHandler = context.pipeline().get(SslHandler.class); - sslHandler.handshakeFuture().addListener(new FutureListener() { - @Override - public void operationComplete(Future future) throws Exception { - if (future.isSuccess()) { - LOG.trace("SSL Handshake has completed: {}", channel); - handleConnected(channel); - } else { - LOG.trace("SSL Handshake has failed: {}", channel); - handleTransportFailure(channel, future.cause()); - } - } - }); - } - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { - ctx.flush(); - } - - @Override - public void channelInactive(ChannelHandlerContext context) throws Exception { - handleTransportFailure(context.channel(), new IOException("Remote closed connection unexpectedly")); - } - - @Override - public void channelExceptionCaught(ChannelHandlerContext context, Throwable cause) throws Exception { - handleTransportFailure(context.channel(), cause); - } - - protected void dispatchReadBuffer(Buffer buffer) throws Exception { - LOG.trace("New data read: {}", buffer); - - // Wrap the buffer and make it read-only as the handlers should not be altering the - // read bytes and if they need to they should be copying them. If the handler doesn't - // take ownership of the incoming buffer then we will close it and the reference count - // will be decremented here (default auto decrement has been disabled). - final ProtonBuffer wrapped = nettyAllocator.wrap(buffer).convertToReadOnly(); - - try (wrapped) { - listener.transportRead(wrapped); - } - } - } - - //----- Handle binary data over socket connections -----------------------// - - protected class NettyTcpTransportHandler extends NettyDefaultHandler { - - @Override - protected void messageReceived(ChannelHandlerContext ctx, Buffer buffer) throws Exception { - dispatchReadBuffer(buffer); - } - } - - @Override - public URI getRemoteURI() { - if (host != null) { - try { - return new URI(getScheme(), null, host, port, null, null, null); - } catch (URISyntaxException e) { - } - } - - return null; - } - - protected String getScheme() { - return isSecure() ? "ssl" : "tcp"; - } -} diff --git a/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/transport/netty5/WebSocketTransport.java b/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/transport/netty5/WebSocketTransport.java deleted file mode 100644 index 62c0097d..00000000 --- a/protonj2-client/src/main/java/org/apache/qpid/protonj2/client/transport/netty5/WebSocketTransport.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.qpid.protonj2.client.transport.netty5; - -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.charset.StandardCharsets; -import java.util.concurrent.TimeUnit; - -import org.apache.qpid.protonj2.client.SslOptions; -import org.apache.qpid.protonj2.client.TransportOptions; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.netty5.bootstrap.Bootstrap; -import io.netty5.buffer.Buffer; -import io.netty5.channel.Channel; -import io.netty5.channel.ChannelHandler; -import io.netty5.channel.ChannelHandlerAdapter; -import io.netty5.channel.ChannelHandlerContext; -import io.netty5.channel.ChannelPipeline; -import io.netty5.handler.codec.http.DefaultHttpContent; -import io.netty5.handler.codec.http.FullHttpResponse; -import io.netty5.handler.codec.http.HttpClientCodec; -import io.netty5.handler.codec.http.HttpObjectAggregator; -import io.netty5.handler.codec.http.headers.HttpHeaders; -import io.netty5.handler.codec.http.websocketx.BinaryWebSocketFrame; -import io.netty5.handler.codec.http.websocketx.CloseWebSocketFrame; -import io.netty5.handler.codec.http.websocketx.ContinuationWebSocketFrame; -import io.netty5.handler.codec.http.websocketx.PingWebSocketFrame; -import io.netty5.handler.codec.http.websocketx.PongWebSocketFrame; -import io.netty5.handler.codec.http.websocketx.TextWebSocketFrame; -import io.netty5.handler.codec.http.websocketx.WebSocketClientHandshaker; -import io.netty5.handler.codec.http.websocketx.WebSocketClientHandshakerFactory; -import io.netty5.handler.codec.http.websocketx.WebSocketFrame; -import io.netty5.handler.codec.http.websocketx.WebSocketVersion; -import io.netty5.handler.codec.http.websocketx.extensions.compression.WebSocketClientCompressionHandler; -import io.netty5.util.concurrent.Future; -import io.netty5.util.concurrent.FutureListener; - -/** - * Netty based WebSockets Transport that wraps and extends the TCP Transport. - */ -public class WebSocketTransport extends TcpTransport { - - private static final Logger LOG = LoggerFactory.getLogger(WebSocketTransport.class); - - private static final String AMQP_SUB_PROTOCOL = "amqp"; - - private Future handshakeTimeoutFuture; - - /** - * Create a new {@link WebSocketTransport} instance with the given configuration. - * - * @param bootstrap - * the {@link Bootstrap} that this transport's IO is bound to. - * @param options - * the {@link TransportOptions} used to configure the socket connection. - * @param sslOptions - * the {@link SslOptions} to use if the options indicate SSL is enabled. - */ - public WebSocketTransport(Bootstrap bootstrap, TransportOptions options, SslOptions sslOptions) { - super(bootstrap, options, sslOptions); - } - - @Override - public URI getRemoteURI() { - if (host != null) { - try { - return new URI(getScheme(), null, host, port, options.webSocketPath(), null, null); - } catch (URISyntaxException e) { - } - } - - return null; - } - - @Override - protected ChannelHandler createChannelHandler() { - return new NettyWebSocketTransportHandler(); - } - - @Override - protected void addAdditionalHandlers(ChannelPipeline pipeline) { - pipeline.addLast(new HttpClientCodec()); - pipeline.addLast(new HttpObjectAggregator(8192)); - if (options.webSocketCompression()) { - pipeline.addLast(WebSocketClientCompressionHandler.INSTANCE); - } - } - - @Override - protected void handleConnected(Channel channel) throws Exception { - LOG.trace("Channel has become active, awaiting WebSocket handshake! Channel is {}", channel); - } - - @Override - protected String getScheme() { - return isSecure() ? "wss" : "ws"; - } - - //----- Handle connection events -----------------------------------------// - - private class OutputBufferToBinaryFrameHandler extends ChannelHandlerAdapter { - - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - return ctx.write(new BinaryWebSocketFrame((Buffer) msg)); - } - } - - private class NettyWebSocketTransportHandler extends NettyDefaultHandler implements ChannelHandler { - - private final WebSocketClientHandshaker handshaker; - - public NettyWebSocketTransportHandler() { - HttpHeaders headers = HttpHeaders.newHeaders(); - - options.webSocketHeaders().forEach((key, value) -> { - headers.set(key, value); - }); - - handshaker = WebSocketClientHandshakerFactory.newHandshaker( - getRemoteURI(), WebSocketVersion.V13, AMQP_SUB_PROTOCOL, - true, headers, options.webSocketMaxFrameSize()); - } - - @Override - public void channelInactive(ChannelHandlerContext context) throws Exception { - if (handshakeTimeoutFuture != null) { - handshakeTimeoutFuture.cancel(); - } - - super.channelInactive(context); - } - - @Override - public void channelActive(ChannelHandlerContext context) throws Exception { - handshaker.handshake(context.channel()).addListener(new FutureListener() { - - @Override - public void operationComplete(Future future) throws Exception { - if (future.isSuccess()) { - // Now we can add a transformer for outbound buffers that will produce - // binary websocket frame wrappers to ensure our writes are encoded. - context.pipeline().addLast(new OutputBufferToBinaryFrameHandler()); - } - } - }); - - handshakeTimeoutFuture = context.executor().schedule(()-> { - LOG.trace("WebSocket handshake timed out! Channel is {}", context.channel()); - if (!handshaker.isHandshakeComplete()) { - WebSocketTransport.super.handleTransportFailure(channel, new IOException("WebSocket handshake timed out")); - } - }, getTransportOptions().connectTimeout(), TimeUnit.MILLISECONDS); - - super.channelActive(context); - } - - @Override - protected void messageReceived(ChannelHandlerContext ctx, Object message) throws Exception { - LOG.trace("New data read: incoming: {}", message); - - Channel ch = ctx.channel(); - if (!handshaker.isHandshakeComplete()) { - handshaker.finishHandshake(ch, (FullHttpResponse) message); - LOG.trace("WebSocket Client connected! {}", ctx.channel()); - // Now trigger super processing as we are really connected. - if (handshakeTimeoutFuture.cancel()) { - WebSocketTransport.super.handleConnected(ch); - } - - return; - } - - // We shouldn't get this since we handle the handshake previously. - if (message instanceof FullHttpResponse) { - FullHttpResponse response = (FullHttpResponse) message; - throw new IllegalStateException( - "Unexpected FullHttpResponse (getStatus=" + response.status() + - ", content=" + response.payload().toString(StandardCharsets.UTF_8) + ')'); - } - - WebSocketFrame frame = (WebSocketFrame) message; - if (frame instanceof TextWebSocketFrame) { - TextWebSocketFrame textFrame = (TextWebSocketFrame) frame; - LOG.warn("WebSocket Client received message: {}", textFrame.text()); - ctx.fireChannelExceptionCaught(new IOException("Received invalid frame over WebSocket.")); - } else if (frame instanceof BinaryWebSocketFrame) { - BinaryWebSocketFrame binaryFrame = (BinaryWebSocketFrame) frame; - LOG.trace("WebSocket Client received data: {} bytes", binaryFrame.binaryData().readableBytes()); - dispatchReadBuffer(binaryFrame.binaryData()); - } else if (frame instanceof ContinuationWebSocketFrame) { - ContinuationWebSocketFrame continuationFrame = (ContinuationWebSocketFrame) frame; - LOG.trace("WebSocket Client received data continuation: {} bytes", continuationFrame.binaryData().readableBytes()); - dispatchReadBuffer(continuationFrame.binaryData()); - } else if (frame instanceof PingWebSocketFrame) { - LOG.trace("WebSocket Client received ping, response with pong"); - ch.write(new PongWebSocketFrame(frame.binaryData())); - } else if (frame instanceof CloseWebSocketFrame) { - LOG.trace("WebSocket Client received closing"); - ch.close(); - } - } - } -} diff --git a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/impl/ReceiverTest.java b/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/impl/ReceiverTest.java index 3bcc3651..e630f484 100644 --- a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/impl/ReceiverTest.java +++ b/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/impl/ReceiverTest.java @@ -37,6 +37,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; @@ -2458,15 +2459,20 @@ public void testCannotReadFromStreamDeliveredBeforeConnectionDrop() throws Excep .withMore(false) .withMessageFormat(0) .withPayload(payload).queue(); - peer.dropAfterLastHandler(); + peer.expectDisposition().optional(); + peer.dropAfterLastHandler(1); peer.start(); URI remoteURI = peer.getServerURI(); LOG.info("Test started, peer listening on: {}", remoteURI); + final CountDownLatch disconnected = new CountDownLatch(1); + Client container = Client.create(); - Connection connection = container.connect(remoteURI.getHost(), remoteURI.getPort()); + ConnectionOptions options = new ConnectionOptions(); + options.disconnectedHandler((c, e) -> disconnected.countDown()); + Connection connection = container.connect(remoteURI.getHost(), remoteURI.getPort(), options); final Receiver receiver = connection.openReceiver("test-queue"); final Delivery delivery = receiver.receive(); @@ -2474,7 +2480,10 @@ public void testCannotReadFromStreamDeliveredBeforeConnectionDrop() throws Excep assertNotNull(delivery); - // Data already read so it will be already available for read. + assertTrue(disconnected.await(5, TimeUnit.SECONDS)); + + // Data already read so it will be already available for read but should be unable to + // since the connection has dropped. assertNotEquals(-1, delivery.rawInputStream().read()); connection.close(); diff --git a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/impl/ReconnectStreamReceiverTest.java b/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/impl/ReconnectStreamReceiverTest.java index 13173783..bc5c3214 100644 --- a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/impl/ReconnectStreamReceiverTest.java +++ b/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/impl/ReconnectStreamReceiverTest.java @@ -105,7 +105,6 @@ public void testStreamReceiverRecoversAndDeliveryReceived() throws Exception { } } - @SuppressWarnings("resource") @Test public void testCannotReceiveFromStreamStartedBeforeReconnection() throws Exception { final byte[] payload = createEncodedMessage(new AmqpValue<>("Hello World")); diff --git a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/impl/ReconnectTest.java b/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/impl/ReconnectTest.java index 03d3fa24..383c5e57 100644 --- a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/impl/ReconnectTest.java +++ b/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/impl/ReconnectTest.java @@ -47,7 +47,7 @@ /** * Test client implementation of basic connection recovery and its configuration. */ -@Timeout(20) +@Timeout(30) public class ReconnectTest extends ImperativeClientTestCase { @Test @@ -112,7 +112,7 @@ public void testConnectionNotifiesReconnectionLifecycleEvents() throws Exception assertTrue(connected.await(5, TimeUnit.SECONDS)); assertTrue(disconnected.await(5, TimeUnit.SECONDS)); assertTrue(reconnected.await(5, TimeUnit.SECONDS)); - assertTrue(failed.await(5, TimeUnit.SECONDS)); + assertTrue(failed.await(20, TimeUnit.SECONDS)); connection.close(); @@ -237,7 +237,7 @@ private void testReconnectStopsAfterSaslPermFailure(byte saslCode) throws Except // Should connect, then fail and attempt to connect to second and third before stopping assertTrue(connected.await(5, TimeUnit.SECONDS)); assertTrue(disconnected.await(5, TimeUnit.SECONDS)); - assertTrue(failed.await(5, TimeUnit.SECONDS)); + assertTrue(failed.await(20, TimeUnit.SECONDS)); assertEquals(1, reconnected.getCount()); connection.close(); diff --git a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/impl/SenderTest.java b/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/impl/SenderTest.java index a16b360c..78fd08e9 100644 --- a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/impl/SenderTest.java +++ b/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/impl/SenderTest.java @@ -1315,7 +1315,7 @@ private void tryReadSenderRemoteProperties(boolean attachResponse) throws Except Session session = connection.openSession(); session.openFuture().get(); - SenderOptions options = new SenderOptions().openTimeout(75); + SenderOptions options = new SenderOptions().openTimeout(100); Sender sender = session.openSender("test-sender", options); peer.waitForScriptToComplete(5, TimeUnit.SECONDS); @@ -1381,13 +1381,9 @@ private void tryReadRemoteOfferedCapabilities(boolean attachResponse) throws Exc LOG.info("Connect test started, peer listening on: {}", remoteURI); Client container = Client.create(); - ConnectionOptions options = new ConnectionOptions().openTimeout(75); + ConnectionOptions options = new ConnectionOptions().openTimeout(100); Connection connection = container.connect(remoteURI.getHost(), remoteURI.getPort(), options); - connection.openFuture().get(); - Session session = connection.openSession(); - session.openFuture().get(); - Sender sender = session.openSender("test-sender"); peer.waitForScriptToComplete(5, TimeUnit.SECONDS); @@ -1449,13 +1445,9 @@ private void tryReadRemoteDesiredCapabilities(boolean attachResponse) throws Exc LOG.info("Connect test started, peer listening on: {}", remoteURI); Client container = Client.create(); - ConnectionOptions options = new ConnectionOptions().openTimeout(75); + ConnectionOptions options = new ConnectionOptions().openTimeout(100); Connection connection = container.connect(remoteURI.getHost(), remoteURI.getPort(), options); - connection.openFuture().get(); - Session session = connection.openSession(); - session.openFuture().get(); - Sender sender = session.openSender("test-sender"); peer.waitForScriptToComplete(5, TimeUnit.SECONDS); @@ -2499,6 +2491,91 @@ public void testWaitForAcceptanceFailsIfRemoteSendsRejected() throws Exception { assertTrue(tracker.remoteSettled()); assertFalse(tracker.remoteState().isAccepted()); + assertTrue(tracker.remoteState().getType().equals(DeliveryState.Type.REJECTED)); + + peer.waitForScriptToComplete(5, TimeUnit.SECONDS); + peer.expectDetach().respond(); + peer.expectClose().respond(); + + sender.closeAsync().get(); + connection.closeAsync().get(); + + peer.waitForScriptToComplete(5, TimeUnit.SECONDS); + } + } + + @Test + public void testWaitForAcceptanceFailsIfRemoteSendsReleased() throws Exception { + try (ProtonTestServer peer = new ProtonTestServer()) { + peer.expectSASLAnonymousConnect(); + peer.expectOpen().respond(); + peer.expectBegin().respond(); + peer.expectAttach().ofSender().respond(); + peer.remoteFlow().withLinkCredit(2).queue(); + peer.expectTransfer().withNonNullPayload().withMore(false).respond().withSettled(true).withState().released(); + peer.start(); + + URI remoteURI = peer.getServerURI(); + + LOG.info("Test started, peer listening on: {}", remoteURI); + + Client container = Client.create(); + Connection connection = container.connect(remoteURI.getHost(), remoteURI.getPort()); + Sender sender = connection.openSender("test-queue").openFuture().get(); + Tracker tracker = sender.send(Message.create("Hello World")); + + try { + tracker.awaitAccepted(10, TimeUnit.SECONDS); + fail("Should not succeed since remote sent something other than Accepted"); + } catch (ClientDeliveryStateException dlvEx) { + // Expected + } + + assertTrue(tracker.remoteSettled()); + assertFalse(tracker.remoteState().isAccepted()); + assertTrue(tracker.remoteState().getType().equals(DeliveryState.Type.RELEASED)); + + peer.waitForScriptToComplete(5, TimeUnit.SECONDS); + peer.expectDetach().respond(); + peer.expectClose().respond(); + + sender.closeAsync().get(); + connection.closeAsync().get(); + + peer.waitForScriptToComplete(5, TimeUnit.SECONDS); + } + } + + @Test + public void testWaitForAcceptanceFailsIfRemoteSendsModified() throws Exception { + try (ProtonTestServer peer = new ProtonTestServer()) { + peer.expectSASLAnonymousConnect(); + peer.expectOpen().respond(); + peer.expectBegin().respond(); + peer.expectAttach().ofSender().respond(); + peer.remoteFlow().withLinkCredit(2).queue(); + peer.expectTransfer().withNonNullPayload().withMore(false).respond().withSettled(true).withState().modified(); + peer.start(); + + URI remoteURI = peer.getServerURI(); + + LOG.info("Test started, peer listening on: {}", remoteURI); + + Client container = Client.create(); + Connection connection = container.connect(remoteURI.getHost(), remoteURI.getPort()); + Sender sender = connection.openSender("test-queue").openFuture().get(); + Tracker tracker = sender.send(Message.create("Hello World")); + + try { + tracker.awaitAccepted(10, TimeUnit.SECONDS); + fail("Should not succeed since remote sent something other than Accepted"); + } catch (ClientDeliveryStateException dlvEx) { + // Expected + } + + assertTrue(tracker.remoteSettled()); + assertFalse(tracker.remoteState().isAccepted()); + assertTrue(tracker.remoteState().getType().equals(DeliveryState.Type.MODIFIED)); peer.waitForScriptToComplete(5, TimeUnit.SECONDS); peer.expectDetach().respond(); diff --git a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/impl/SslConnectionTest.java b/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/impl/SslConnectionTest.java index 93c5c395..305dbe0c 100644 --- a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/impl/SslConnectionTest.java +++ b/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/impl/SslConnectionTest.java @@ -39,7 +39,7 @@ import org.apache.qpid.protonj2.client.ConnectionOptions; import org.apache.qpid.protonj2.client.SslOptions; import org.apache.qpid.protonj2.client.test.ImperativeClientTestCase; -import org.apache.qpid.protonj2.client.transport.netty5.SslSupport; +import org.apache.qpid.protonj2.client.transport.netty4.SslSupport; import org.apache.qpid.protonj2.test.driver.ProtonTestServer; import org.apache.qpid.protonj2.test.driver.ProtonTestServerOptions; import org.apache.qpid.protonj2.types.security.SaslCode; @@ -50,7 +50,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import io.netty5.handler.ssl.OpenSsl; +import io.netty.handler.ssl.OpenSsl; /** * Test for the Connection class diff --git a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/impl/StreamSenderTest.java b/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/impl/StreamSenderTest.java index ab69063c..3a0b31ca 100644 --- a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/impl/StreamSenderTest.java +++ b/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/impl/StreamSenderTest.java @@ -297,7 +297,6 @@ public void testOpenStreamSenderAppliesConfiguredSessionOutgoingWindow() throws } } - @SuppressWarnings("resource") @Test public void testSendCustomMessageWithMultipleAmqpValueSections() throws Exception { try (ProtonTestServer peer = new ProtonTestServer()) { @@ -397,7 +396,6 @@ public void testSendCustomMessageWithMultipleAmqpValueSections() throws Exceptio } } - @SuppressWarnings("resource") @Test public void testClearBodySectionsIsNoOpForStreamSenderMessage() throws Exception { try (ProtonTestServer peer = new ProtonTestServer()) { @@ -1478,7 +1476,6 @@ void testCompleteStreamClosureCausesTransferCompleted() throws Exception { } } - @SuppressWarnings("resource") @Test void testRawOutputStreamFromMessageWritesUnmodifiedBytes() throws Exception { try (ProtonTestServer peer = new ProtonTestServer()) { @@ -1677,7 +1674,6 @@ public void testStreamSenderMessageWithDeliveryAnnotations() throws Exception { } } - @SuppressWarnings("resource") @Test public void testStreamSenderWritesFooterAfterStreamClosed() throws Exception { try (ProtonTestServer peer = new ProtonTestServer()) { @@ -1893,7 +1889,6 @@ void testAutoFlushDuringMessageSendThatExceedConfiguredBufferLimitSessionCreditL }); peer.waitForScriptToComplete(5, TimeUnit.SECONDS); - peer.remoteFlow().withIncomingWindow(1).withNextIncomingId(1).withLinkCredit(10).now(); peer.expectTransfer().withNonNullPayload().withMore(true); peer.remoteFlow().withIncomingWindow(1).withNextIncomingId(2).withLinkCredit(10).queue(); peer.expectTransfer().withNonNullPayload().withMore(true); @@ -1904,6 +1899,9 @@ void testAutoFlushDuringMessageSendThatExceedConfiguredBufferLimitSessionCreditL peer.remoteFlow().withIncomingWindow(1).withNextIncomingId(5).withLinkCredit(10).queue(); peer.expectTransfer().withNonNullPayload().withMore(false).accept(); + // Initiate message flow by now granting the first credit + peer.remoteFlow().withIncomingWindow(1).withNextIncomingId(1).withLinkCredit(10).now(); + peer.waitForScriptToComplete(5, TimeUnit.SECONDS); peer.expectDetach().respond(); peer.expectEnd().respond(); @@ -2037,11 +2035,13 @@ void testConcurrentMessageSendsBlocksBehindSendWaitingForCredit() throws Excepti }); peer.waitForScriptToComplete(5, TimeUnit.SECONDS); - peer.remoteFlow().withIncomingWindow(1).withDeliveryCount(0).withNextIncomingId(1).withLinkCredit(1).now(); peer.expectTransfer().withNonNullPayload().withMore(false).respond().withSettled(true).withState().accepted(); peer.remoteFlow().withIncomingWindow(1).withDeliveryCount(1).withNextIncomingId(2).withLinkCredit(1).queue(); peer.expectTransfer().withNonNullPayload().withMore(false).respond().withSettled(true).withState().accepted(); + // Initiate message flow by now granting the first credit + peer.remoteFlow().withIncomingWindow(1).withDeliveryCount(0).withNextIncomingId(1).withLinkCredit(1).now(); + assertTrue(send2Completed.await(10, TimeUnit.SECONDS)); peer.waitForScriptToComplete(5, TimeUnit.SECONDS); @@ -2108,7 +2108,6 @@ void testConcurrentMessageSendWaitingOnSplitFramedSendToCompleteIsSentAfterCredi }); peer.waitForScriptToComplete(5, TimeUnit.SECONDS); - peer.remoteFlow().withIncomingWindow(1).withDeliveryCount(0).withNextIncomingId(1).withLinkCredit(1).now(); peer.expectTransfer().withNonNullPayload().withMore(true); peer.remoteFlow().withIncomingWindow(1).withDeliveryCount(0).withNextIncomingId(2).withLinkCredit(1).queue(); peer.expectTransfer().withNonNullPayload().withMore(false).respond().withSettled(true).withState().accepted(); @@ -2117,6 +2116,9 @@ void testConcurrentMessageSendWaitingOnSplitFramedSendToCompleteIsSentAfterCredi peer.remoteFlow().withIncomingWindow(1).withDeliveryCount(1).withNextIncomingId(4).withLinkCredit(1).queue(); peer.expectTransfer().withNonNullPayload().withMore(false).respond().withSettled(true).withState().accepted(); + // Initiate message flow by now granting the first credits + peer.remoteFlow().withIncomingWindow(1).withDeliveryCount(0).withNextIncomingId(1).withLinkCredit(1).now(); + assertTrue(send2Completed.await(10, TimeUnit.SECONDS)); peer.waitForScriptToComplete(5, TimeUnit.SECONDS); @@ -2133,7 +2135,6 @@ void testConcurrentMessageSendWaitingOnSplitFramedSendToCompleteIsSentAfterCredi } } - @SuppressWarnings("resource") @Test void testMessageSendWhileStreamSendIsOpenShouldBlock() throws Exception { try (ProtonTestServer peer = new ProtonTestServer()) { @@ -2199,7 +2200,6 @@ void testMessageSendWhileStreamSendIsOpenShouldBlock() throws Exception { } } - @SuppressWarnings("resource") @Test public void testStreamSenderSessionCannotCreateNewResources() throws Exception { try (ProtonTestServer peer = new ProtonTestServer()) { diff --git a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/NettyBlackHoleServer.java b/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/NettyBlackHoleServer.java deleted file mode 100644 index 67b5d72d..00000000 --- a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/NettyBlackHoleServer.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.qpid.protonj2.client.transport.netty5; - -import org.apache.qpid.protonj2.client.SslOptions; -import org.apache.qpid.protonj2.client.TransportOptions; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.netty5.channel.ChannelHandler; -import io.netty5.channel.ChannelHandlerAdapter; -import io.netty5.channel.ChannelHandlerContext; - -public class NettyBlackHoleServer extends NettyServer { - - private static final Logger LOG = LoggerFactory.getLogger(NettyBlackHoleServer.class); - - public NettyBlackHoleServer(TransportOptions options, SslOptions sslOptions) { - super(options, sslOptions); - } - - public NettyBlackHoleServer(TransportOptions options, SslOptions sslOptions, boolean needClientAuth) { - super(options, sslOptions, needClientAuth); - } - - @Override - protected ChannelHandler getServerHandler() { - return new BlackHoleInboundHandler(); - } - - private class BlackHoleInboundHandler extends ChannelHandlerAdapter { - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - LOG.trace("BlackHoleInboundHandler: Channel read, dropping: {}", msg); - } - } -} \ No newline at end of file diff --git a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/NettyEchoServer.java b/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/NettyEchoServer.java deleted file mode 100644 index ba86b7da..00000000 --- a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/NettyEchoServer.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.qpid.protonj2.client.transport.netty5; - -import org.apache.qpid.protonj2.client.SslOptions; -import org.apache.qpid.protonj2.client.TransportOptions; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.netty5.buffer.Buffer; -import io.netty5.channel.ChannelHandler; -import io.netty5.channel.ChannelHandlerContext; -import io.netty5.channel.SimpleChannelInboundHandler; - -/** - * Simple Netty Server used to echo all data. - */ -public class NettyEchoServer extends NettyServer { - - private static final Logger LOG = LoggerFactory.getLogger(NettyEchoServer.class); - - public NettyEchoServer(TransportOptions options, SslOptions sslOptions, boolean needClientAuth) { - super(options, sslOptions, needClientAuth); - } - - @Override - protected ChannelHandler getServerHandler() { - return new EchoServerHandler(); - } - - private class EchoServerHandler extends SimpleChannelInboundHandler { - - @Override - public void messageReceived(ChannelHandlerContext ctx, Buffer msg) { - LOG.trace("Channel read: {}", msg); - ctx.write(msg.copy()); - } - } -} diff --git a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/NettyIOContextTest.java b/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/NettyIOContextTest.java deleted file mode 100644 index 1affaeb6..00000000 --- a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/NettyIOContextTest.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.qpid.protonj2.client.transport.netty5; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.apache.qpid.protonj2.client.SslOptions; -import org.apache.qpid.protonj2.client.TransportOptions; -import org.junit.jupiter.api.Test; - -class NettyIOContextTest { - - @Test - void testCannotCreateNewTransportFromShutdownBootStrap() { - Netty5IOContext context = new Netty5IOContext(new TransportOptions(), new SslOptions(), "test"); - - context.shutdown(); - - assertThrows(IllegalStateException.class, () -> context.newTransport()); - } - - @Test - void testEventLoopGroupAccessibleAfterCreate() { - Netty5IOContext context = new Netty5IOContext(new TransportOptions(), new SslOptions(), "test"); - - assertNotNull(context.ioScheduler()); - assertFalse(context.ioScheduler().isShutdown()); - - context.shutdown(); - - assertTrue(context.ioScheduler().isShutdown()); - } -} diff --git a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/NettyServer.java b/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/NettyServer.java deleted file mode 100644 index ce3a75be..00000000 --- a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/NettyServer.java +++ /dev/null @@ -1,410 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.qpid.protonj2.client.transport.netty5; - -import java.net.InetSocketAddress; -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.util.Set; -import java.util.concurrent.ConcurrentSkipListSet; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLEngine; - -import org.apache.qpid.protonj2.client.SslOptions; -import org.apache.qpid.protonj2.client.TransportOptions; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.netty5.bootstrap.ServerBootstrap; -import io.netty5.buffer.Buffer; -import io.netty5.buffer.BufferAllocator; -import io.netty5.buffer.DefaultBufferAllocators; -import io.netty5.channel.Channel; -import io.netty5.channel.ChannelHandler; -import io.netty5.channel.ChannelHandlerAdapter; -import io.netty5.channel.ChannelHandlerContext; -import io.netty5.channel.ChannelInitializer; -import io.netty5.channel.ChannelOption; -import io.netty5.channel.EventLoopGroup; -import io.netty5.channel.MultithreadEventLoopGroup; -import io.netty5.channel.nio.NioHandler; -import io.netty5.channel.socket.nio.NioServerSocketChannel; -import io.netty5.handler.codec.http.DefaultFullHttpResponse; -import io.netty5.handler.codec.http.DefaultHttpContent; -import io.netty5.handler.codec.http.FullHttpRequest; -import io.netty5.handler.codec.http.FullHttpResponse; -import io.netty5.handler.codec.http.HttpObjectAggregator; -import io.netty5.handler.codec.http.HttpResponseStatus; -import io.netty5.handler.codec.http.HttpServerCodec; -import io.netty5.handler.codec.http.HttpUtil; -import io.netty5.handler.codec.http.HttpVersion; -import io.netty5.handler.codec.http.websocketx.BinaryWebSocketFrame; -import io.netty5.handler.codec.http.websocketx.ContinuationWebSocketFrame; -import io.netty5.handler.codec.http.websocketx.WebSocketFrame; -import io.netty5.handler.codec.http.websocketx.WebSocketServerHandshakeCompletionEvent; -import io.netty5.handler.codec.http.websocketx.WebSocketServerProtocolHandler; -import io.netty5.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler; -import io.netty5.handler.logging.LogLevel; -import io.netty5.handler.logging.LoggingHandler; -import io.netty5.handler.ssl.SslHandler; -import io.netty5.util.concurrent.Future; -import io.netty5.util.concurrent.FutureListener; - -/** - * Base Server implementation used to create Netty based server implementations for - * unit testing aspects of the client code. - */ -public abstract class NettyServer implements AutoCloseable { - - private static final Logger LOG = LoggerFactory.getLogger(NettyServer.class); - - static final String WEBSOCKET_PATH = "/"; - static final int SERVER_CHOOSES_PORT = 0; - - private EventLoopGroup bossGroup; - private EventLoopGroup workerGroup; - private Channel serverChannel; - private final TransportOptions options; - private final SslOptions sslOptions; - private int serverPort = SERVER_CHOOSES_PORT; - private final boolean needClientAuth; - private int maxFrameSize = TransportOptions.DEFAULT_WEBSOCKET_MAX_FRAME_SIZE; - private String webSocketPath = WEBSOCKET_PATH; - private volatile boolean fragmentWrites; - private volatile SslHandler sslHandler; - private volatile WebSocketServerHandshakeCompletionEvent handshakeComplete; - private final CountDownLatch handshakeCompletion = new CountDownLatch(1); - - private final AtomicBoolean started = new AtomicBoolean(); - - public NettyServer(TransportOptions options, SslOptions sslOptions) { - this(options, sslOptions, false); - } - - public NettyServer(TransportOptions options, SslOptions sslOptions, boolean needClientAuth) { - this.sslOptions = sslOptions; - this.options = options; - this.needClientAuth = needClientAuth; - } - - public boolean isSecureServer() { - return sslOptions.sslEnabled(); - } - - public boolean isWebSocketServer() { - return options.useWebSockets(); - } - - public boolean isUseWebSocketCompression() { - return options.webSocketCompression(); - } - - public String getWebSocketPath() { - return webSocketPath; - } - - public void setWebSocketPath(String webSocketPath) { - this.webSocketPath = webSocketPath; - } - - public int getMaxFrameSize() { - return maxFrameSize; - } - - public void setMaxFrameSize(int maxFrameSize) { - this.maxFrameSize = maxFrameSize; - } - - public void setFragmentWrites(boolean fragmentWrites) { - if(!isWebSocketServer()) { - throw new IllegalStateException("Only applicable to WebSocket servers"); - } - - this.fragmentWrites = fragmentWrites; - } - - public boolean isFragmentWrites() { - return fragmentWrites; - } - - public boolean awaitHandshakeCompletion(long delayMs) throws InterruptedException { - return handshakeCompletion.await(delayMs, TimeUnit.MILLISECONDS); - } - - public WebSocketServerHandshakeCompletionEvent getHandshakeComplete() { - return handshakeComplete; - } - - protected URI getConnectionURI() throws Exception { - if (!started.get()) { - throw new IllegalStateException("Cannot get URI of non-started server"); - } - - int port = getServerPort(); - - String scheme; - String path; - - if (isWebSocketServer()) { - if (isSecureServer()) { - scheme = "amqpwss"; - } else { - scheme = "amqpws"; - } - } else { - if (isSecureServer()) { - scheme = "amqps"; - } else { - scheme = "amqp"; - } - } - - if (isWebSocketServer()) { - path = getWebSocketPath(); - } else { - path = null; - } - - return new URI(scheme, null, "localhost", port, path, null, null); - } - - public void start() throws Exception { - start(serverPort); - } - - public void start(int listenOn) throws Exception { - if (started.compareAndSet(false, true)) { - - // Configure the server. - bossGroup = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - - ServerBootstrap server = new ServerBootstrap(); - server.group(bossGroup, workerGroup); - server.channel(NioServerSocketChannel.class); - server.option(ChannelOption.SO_BACKLOG, 100); - server.handler(new LoggingHandler(LogLevel.INFO)); - server.childHandler(new ChannelInitializer<>() { - - @Override - public void initChannel(Channel ch) throws Exception { - if (isSecureServer()) { - SSLContext context = SslSupport.createJdkSslContext(sslOptions); - SSLEngine engine = SslSupport.createJdkSslEngine(null, -1, context, sslOptions); - engine.setUseClientMode(false); - engine.setNeedClientAuth(needClientAuth); - sslHandler = new SslHandler(engine); - ch.pipeline().addLast(sslHandler); - } - - if (options.traceBytes()) { - ch.pipeline().addLast("logger", new LoggingHandler(getClass())); - } - - if (isWebSocketServer()) { - ch.pipeline().addLast(new HttpServerCodec()); - ch.pipeline().addLast(new HttpObjectAggregator(65536)); - if (isUseWebSocketCompression()) { - ch.pipeline().addLast(new WebSocketServerCompressionHandler()); - } - ch.pipeline().addLast(new WebSocketServerProtocolHandler(getWebSocketPath(), "amqp", true, maxFrameSize)); - } - - ch.pipeline().addLast(new NettyServerOutboundHandler()); - ch.pipeline().addLast(new NettyServerInboundHandler()); - ch.pipeline().addLast(getServerHandler()); - } - }); - - // Start the server. - serverChannel = server.bind(getServerPort()).asStage().get(); - serverPort = ((InetSocketAddress) serverChannel.localAddress()).getPort(); - } - } - - protected abstract ChannelHandler getServerHandler(); - - public void stop() throws InterruptedException { - if (started.compareAndSet(true, false)) { - try { - LOG.info("Sync on server channel close"); - serverChannel.close().asStage().await(); - } catch (InterruptedException e) { - } - - for (Channel channel : clients) { - try { - channel.close().asStage().await(); - } catch (Exception ex) {} - } - - clients.clear(); - - // Shut down all event loops to terminate all threads. - LOG.trace("Shutting down boss group"); - bossGroup.shutdownGracefully(0, 2, TimeUnit.MILLISECONDS); - bossGroup.awaitTermination(3, TimeUnit.MILLISECONDS); - LOG.trace("Boss group shut down"); - - LOG.trace("Shutting down worker group"); - workerGroup.shutdownGracefully(0, 2, TimeUnit.MILLISECONDS); - workerGroup.awaitTermination(3, TimeUnit.MILLISECONDS); - LOG.trace("Worker group shut down"); - } - } - - @Override - public void close() throws InterruptedException { - stop(); - } - - public int getServerPort() { - if (!started.get()) { - throw new IllegalStateException("Cannot get server port of non-started server"); - } - - return serverPort; - } - - public int getClientCount() { - return clients.size(); - } - - private class NettyServerOutboundHandler extends ChannelHandlerAdapter { - - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - LOG.trace("NettyServerHandler: Channel write: {}", msg); - if (isWebSocketServer() && msg instanceof Buffer) { - if (isFragmentWrites()) { - Buffer orig = (Buffer) msg; - int origIndex = orig.readerOffset(); - int split = orig.readableBytes()/2; - - Buffer part1 = orig.copy(origIndex, split); - LOG.trace("NettyServerHandler: Part1: {}", part1); - orig.readerOffset(origIndex + split); - LOG.trace("NettyServerHandler: Part2: {}", orig); - - BinaryWebSocketFrame frame1 = new BinaryWebSocketFrame(false, 0, part1); - ctx.writeAndFlush(frame1); - ContinuationWebSocketFrame frame2 = new ContinuationWebSocketFrame(true, 0, orig); - return ctx.write(frame2); - } else { - BinaryWebSocketFrame frame = new BinaryWebSocketFrame((Buffer) msg); - return ctx.write(frame); - } - } else { - return ctx.write(msg); - } - } - } - - private static final Set clients = new ConcurrentSkipListSet<>(); - - private class NettyServerInboundHandler extends ChannelHandlerAdapter { - - @Override - public void channelInboundEvent(ChannelHandlerContext context, Object payload) { - if (payload instanceof WebSocketServerHandshakeCompletionEvent) { - handshakeComplete = (WebSocketServerHandshakeCompletionEvent) payload; - handshakeCompletion.countDown(); - } - } - - @Override - public void channelActive(final ChannelHandlerContext ctx) { - LOG.info("NettyServerHandler -> New active channel: {}", ctx.channel()); - SslHandler handler = ctx.pipeline().get(SslHandler.class); - if (handler != null) { - handler.handshakeFuture().addListener(new FutureListener() { - @Override - public void operationComplete(Future future) throws Exception { - LOG.info("Server -> SSL handshake completed. Succeeded: {}", future.isSuccess()); - if (!future.isSuccess()) { - ctx.close(); - } - } - }); - } - - clients.add(ctx.channel()); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - LOG.info("NettyServerHandler: channel has gone inactive: {}", ctx.channel()); - ctx.close(); - clients.remove(ctx.channel()); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - LOG.trace("NettyServerHandler: Channel read: {}", msg); - if (msg instanceof WebSocketFrame) { - WebSocketFrame frame = (WebSocketFrame) msg; - ctx.fireChannelRead(frame.binaryData()); - } else if (msg instanceof FullHttpRequest) { - // Reject anything not on the WebSocket path - FullHttpRequest request = (FullHttpRequest) msg; - sendHttpResponse(ctx, request, - new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST, DefaultBufferAllocators.onHeapAllocator().allocate(0))); - } else { - // Forward anything else along to the next handler. - ctx.fireChannelRead(msg); - } - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) { - ctx.flush(); - } - - @Override - public void channelExceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - LOG.info("NettyServerHandler: NettyServerHandlerException caught on channel: {}", ctx.channel()); - // Close the connection when an exception is raised. - cause.printStackTrace(); - ctx.close(); - } - } - - private static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest request, FullHttpResponse response) { - // Generate an error page if response getStatus code is not OK (200). - if (response.status().code() != 200) { - try (Buffer buf = BufferAllocator.onHeapUnpooled().copyOf(response.status().toString().getBytes(StandardCharsets.UTF_8))) { - response.payload().writeBytes(buf); - HttpUtil.setContentLength(response, response.payload().readableBytes()); - } - } - - // Send the response and close the connection if necessary. - Future f = ctx.channel().writeAndFlush(response); - if (!HttpUtil.isKeepAlive(request) || response.status().code() != 200) { - f.addListener((channel) -> ctx.channel().close()); - } - } - - protected SslHandler getSslHandler() { - return sslHandler; - } -} diff --git a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/OpenSslTransportTest.java b/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/OpenSslTransportTest.java deleted file mode 100644 index 0d4c7ae3..00000000 --- a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/OpenSslTransportTest.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.qpid.protonj2.client.transport.netty5; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; -import static org.junit.jupiter.api.Assumptions.assumeTrue; - -import java.lang.reflect.Field; - -import javax.net.ssl.SSLContext; - -import org.apache.qpid.protonj2.client.SslOptions; -import org.apache.qpid.protonj2.client.transport.Transport; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.netty5.channel.Channel; -import io.netty5.channel.ChannelHandler; -import io.netty5.handler.ssl.OpenSsl; -import io.netty5.handler.ssl.OpenSslEngine; -import io.netty5.handler.ssl.SslHandler; - -/** - * Test basic functionality of the Netty based TCP Transport running in secure mode (SSL). - */ -@Timeout(30) -public class OpenSslTransportTest extends SslTransportTest { - - private static final Logger LOG = LoggerFactory.getLogger(OpenSslTransportTest.class); - - @Test - public void testConnectToServerWithOpenSSLEnabled() throws Exception { - doTestOpenSSLSupport(true); - } - - @Test - public void testConnectToServerWithOpenSSLDisabled() throws Exception { - doTestOpenSSLSupport(false); - } - - private void doTestOpenSSLSupport(boolean useOpenSSL) throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - - try (NettyEchoServer server = createEchoServer()) { - server.start(); - - final int port = server.getServerPort(); - - SslOptions options = createSSLOptions(); - options.allowNativeSSL(useOpenSSL); - - Transport transport = createTransport(createTransportOptions(), options); - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - LOG.info("Connected to server:{}:{} as expected.", HOSTNAME, port); - } catch (Exception e) { - fail("Should have connected to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - - assertTrue(transport.isConnected()); - assertEquals(HOSTNAME, transport.getHost()); - assertEquals(port, transport.getPort()); - assertOpenSSL("Transport should be using OpenSSL", useOpenSSL, transport); - - transport.close(); - - // Additional close should not fail or cause other problems. - transport.close(); - } - - assertTrue(!transportErrored); // Normal shutdown does not trigger the event. - assertTrue(exceptions.isEmpty()); - assertTrue(data.isEmpty()); - } - - @Test - public void testConnectToServerWithUserSuppliedSSLContextWorksWhenOpenSSLRequested() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - - try (NettyEchoServer server = createEchoServer()) { - server.start(); - - final int port = server.getServerPort(); - - SslOptions options = new SslOptions(); - - options.sslEnabled(true); - options.keyStoreLocation(CLIENT_KEYSTORE); - options.keyStorePassword(PASSWORD); - options.trustStoreLocation(CLIENT_TRUSTSTORE); - options.trustStorePassword(PASSWORD); - options.storeType(KEYSTORE_TYPE); - - SSLContext sslContext = SslSupport.createJdkSslContext(options); - - options = new SslOptions(); - options.sslEnabled(true); - options.verifyHost(false); - options.allowNativeSSL(true); - options.sslContextOverride(sslContext); - - Transport transport = createTransport(createTransportOptions(), options); - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - LOG.info("Connected to server:{} as expected.", HOSTNAME, port); - } catch (Exception e) { - fail("Should have connected to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - - assertTrue(transport.isConnected()); - assertEquals(HOSTNAME, transport.getHost(), "Server host is incorrect"); - assertEquals(port, transport.getPort(), "Server port is incorrect"); - assertOpenSSL("Transport should not be using OpenSSL", false, transport); - - transport.close(); - - // Additional close should not fail or cause other problems. - transport.close(); - } - - assertTrue(!transportErrored); // Normal shutdown does not trigger the event. - assertTrue(exceptions.isEmpty()); - assertTrue(data.isEmpty()); - } - - private void assertOpenSSL(String message, boolean expected, Transport transport) throws Exception { - Field channel = null; - Class transportType = transport.getClass(); - - while (transportType != null && channel == null) { - try { - channel = transportType.getDeclaredField("channel"); - } catch (NoSuchFieldException error) { - transportType = transportType.getSuperclass(); - if (Object.class.equals(transportType)) { - transportType = null; - } - } - } - - assertNotNull(channel, "Transport implementation unknown"); - - channel.setAccessible(true); - - Channel activeChannel = (Channel) channel.get(transport) ; - ChannelHandler handler = activeChannel.pipeline().get("ssl"); - assertNotNull(handler, "Channel should have an SSL Handler registered"); - assertTrue(handler instanceof SslHandler); - SslHandler sslHandler = (SslHandler) handler; - - if (expected) { - assertTrue(sslHandler.engine() instanceof OpenSslEngine, message); - } else { - assertFalse(sslHandler.engine() instanceof OpenSslEngine, message); - } - } - - @Override - @Disabled("Can't apply keyAlias in Netty OpenSSL impl") - @Test - public void testConnectWithSpecificClientAuthKeyAlias1() throws Exception { - // TODO - Revert to superclass version if keyAlias becomes supported for Netty. - } - - @Override - @Disabled("Can't apply keyAlias in Netty OpenSSL impl") - @Test - public void testConnectWithSpecificClientAuthKeyAlias2() throws Exception { - // TODO - Revert to superclass version if keyAlias becomes supported for Netty. - } - - @Override - protected SslOptions createSSLOptionsIsVerify(boolean verifyHost) { - SslOptions options = new SslOptions(); - - options.sslEnabled(true); - options.allowNativeSSL(true); - options.keyStoreLocation(CLIENT_KEYSTORE); - options.keyStorePassword(PASSWORD); - options.trustStoreLocation(CLIENT_TRUSTSTORE); - options.trustStorePassword(PASSWORD); - options.storeType(KEYSTORE_TYPE); - options.verifyHost(verifyHost); - - return options; - } - - @Override - protected SslOptions createSSLOptionsWithoutTrustStore(boolean trustAll) { - SslOptions options = new SslOptions(); - - options.sslEnabled(true); - options.storeType(KEYSTORE_TYPE); - options.allowNativeSSL(true); - options.trustAll(trustAll); - - return options; - } -} diff --git a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/SecureWebSocketTransportTest.java b/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/SecureWebSocketTransportTest.java deleted file mode 100644 index 992679c8..00000000 --- a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/SecureWebSocketTransportTest.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.qpid.protonj2.client.transport.netty5; - -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -import org.apache.qpid.protonj2.client.TransportOptions; -import org.apache.qpid.protonj2.client.transport.Transport; -import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Test the WebSocketTransport with channel level security enabled. - */ -public class SecureWebSocketTransportTest extends SslTransportTest { - - private static final Logger LOG = LoggerFactory.getLogger(SecureWebSocketTransportTest.class); - - @Test - public void testEnsureNettyIOContextCreatesWebSocketTransport() throws Exception { - try (NettyEchoServer server = createEchoServer()) { - server.start(); - - final int port = server.getServerPort(); - - Transport transport = createTransport(createTransportOptions(), createSSLOptions()); - - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - } catch (Exception e) { - LOG.info("Failed to connect to: {}:{} as expected.", HOSTNAME, port); - fail("Should not have failed to connect to the server: " + HOSTNAME + ":" + port); - } - - assertTrue(transport instanceof WebSocketTransport); - assertTrue(transport.isConnected()); - - transport.close(); - } - - assertTrue(!transportErrored); // Normal shutdown does not trigger the event. - assertTrue(exceptions.isEmpty()); - assertTrue(data.isEmpty()); - } - - @Override - protected TransportOptions createTransportOptions() { - return new TransportOptions().useWebSockets(true); - } - - @Override - protected TransportOptions createServerTransportOptions() { - return new TransportOptions().useWebSockets(true); - } -} diff --git a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/SslSupportTest.java b/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/SslSupportTest.java deleted file mode 100644 index 5984ff3b..00000000 --- a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/SslSupportTest.java +++ /dev/null @@ -1,1020 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.qpid.protonj2.client.transport.netty5; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; -import static org.junit.jupiter.api.Assumptions.assumeTrue; - -import java.io.IOException; -import java.security.UnrecoverableKeyException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLEngine; - -import org.apache.qpid.protonj2.client.SslOptions; -import org.apache.qpid.protonj2.client.test.ImperativeClientTestCase; -import org.junit.jupiter.api.Test; - -import io.netty5.buffer.BufferAllocator; -import io.netty5.handler.ssl.OpenSsl; -import io.netty5.handler.ssl.OpenSslEngine; -import io.netty5.handler.ssl.SslContext; -import io.netty5.handler.ssl.SslHandler; - -/** - * Tests for the TransportSupport class. - */ -@SuppressWarnings("deprecation") -public class SslSupportTest extends ImperativeClientTestCase { - - public static final String PASSWORD = "password"; - - public static final String HOSTNAME = "localhost"; - - public static final String BROKER_JKS_KEYSTORE = "src/test/resources/broker-jks.keystore"; - public static final String BROKER_JKS_TRUSTSTORE = "src/test/resources/broker-jks.truststore"; - public static final String CLIENT_JKS_KEYSTORE = "src/test/resources/client-jks.keystore"; - public static final String CLIENT_JKS_TRUSTSTORE = "src/test/resources/client-jks.truststore"; - - public static final String BROKER_JCEKS_KEYSTORE = "src/test/resources/broker-jceks.keystore"; - public static final String BROKER_JCEKS_TRUSTSTORE = "src/test/resources/broker-jceks.truststore"; - public static final String CLIENT_JCEKS_KEYSTORE = "src/test/resources/client-jceks.keystore"; - public static final String CLIENT_JCEKS_TRUSTSTORE = "src/test/resources/client-jceks.truststore"; - - public static final String BROKER_PKCS12_KEYSTORE = "src/test/resources/broker-pkcs12.keystore"; - public static final String BROKER_PKCS12_TRUSTSTORE = "src/test/resources/broker-pkcs12.truststore"; - public static final String CLIENT_PKCS12_KEYSTORE = "src/test/resources/client-pkcs12.keystore"; - public static final String CLIENT_PKCS12_TRUSTSTORE = "src/test/resources/client-pkcs12.truststore"; - - public static final String KEYSTORE_JKS_TYPE = "jks"; - public static final String KEYSTORE_JCEKS_TYPE = "jceks"; - public static final String KEYSTORE_PKCS12_TYPE = "pkcs12"; - - public static final String[] ENABLED_PROTOCOLS = new String[] { "TLSv1" }; - - // Currently the OpenSSL implementation cannot disable SSLv2Hello - public static final String[] ENABLED_OPENSSL_PROTOCOLS = new String[] { "SSLv2Hello", "TLSv1" }; - - private static final String ALIAS_DOES_NOT_EXIST = "alias.does.not.exist"; - private static final String ALIAS_CA_CERT = "ca"; - - @Test - public void testLegacySslProtocolsDisabledByDefaultJDK() throws Exception { - SslOptions options = createJksSslOptions(null); - - SSLContext context = SslSupport.createJdkSslContext(options); - assertNotNull(context); - - SSLEngine engine = SslSupport.createJdkSslEngine(null, -1, context, options); - assertNotNull(engine); - - List engineProtocols = Arrays.asList(engine.getEnabledProtocols()); - assertFalse(engineProtocols.contains("SSLv3"), "SSLv3 should not be enabled by default"); - assertFalse(engineProtocols.contains("SSLv2Hello"), "SSLv2Hello should not be enabled by default"); - } - - @Test - public void testLegacySslProtocolsDisabledByDefaultOpenSSL() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - - SslOptions options = createJksSslOptions(null); - - SslContext context = SslSupport.createOpenSslContext(options); - assertNotNull(context); - - SSLEngine engine = SslSupport.createOpenSslEngine(BufferAllocator.onHeapUnpooled(), null, -1, context, options); - assertNotNull(engine); - - List engineProtocols = Arrays.asList(engine.getEnabledProtocols()); - assertFalse(engineProtocols.contains("SSLv3"), "SSLv3 should not be enabled by default"); - - // TODO - Netty is currently unable to disable OpenSSL SSLv2Hello so we are stuck with it for now. - // assertFalse(engineProtocols.contains("SSLv2Hello"), "SSLv2Hello should not be enabled by default"); - } - - @Test - public void testCreateSslContextJksStoreJDK() throws Exception { - SslOptions options = createJksSslOptions(); - - SSLContext context = SslSupport.createJdkSslContext(options); - assertNotNull(context); - - assertEquals("TLS", context.getProtocol()); - } - - @Test - public void testCreateSslContextJksStoreOpenSSL() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - - SslOptions options = createJksSslOptions(); - - SslContext context = SslSupport.createOpenSslContext(options); - assertNotNull(context); - - // TODO There is no means currently of getting the protocol from the netty SslContext. - // assertEquals("TLS", context.getProtocol()); - } - - @Test - public void testCreateSslContextJksStoreWithConfiguredContextProtocolJDK() throws Exception { - SslOptions options = createJksSslOptions(); - String contextProtocol = "TLSv1.2"; - options.contextProtocol(contextProtocol); - - SSLContext context = SslSupport.createJdkSslContext(options); - assertNotNull(context); - - assertEquals(contextProtocol, context.getProtocol()); - } - - @Test - public void testCreateSslContextJksStoreWithConfiguredContextProtocolOpenSSL() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - - SslOptions options = createJksSslOptions(); - String contextProtocol = "TLSv1.2"; - options.contextProtocol(contextProtocol); - - SslContext context = SslSupport.createOpenSslContext(options); - assertNotNull(context); - - // TODO There is no means currently of getting the protocol from the netty SslContext. - // assertEquals(contextProtocol, context.getProtocol()); - } - - @Test - public void testCreateSslContextNoKeyStorePasswordJDK() throws Exception { - SslOptions options = createJksSslOptions(); - options.keyStorePassword(null); - - assertThrows(UnrecoverableKeyException.class, () -> { - SslSupport.createJdkSslContext(options); - }); - } - - @Test - public void testCreateSslContextNoKeyStorePasswordOpenSSL() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - - SslOptions options = createJksSslOptions(); - options.keyStorePassword(null); - - assertThrows(UnrecoverableKeyException.class, () -> { - SslSupport.createOpenSslContext(options); - }); - } - - @Test - public void testCreateSslContextWrongKeyStorePasswordJDK() throws Exception { - SslOptions options = createJksSslOptions(); - options.keyStorePassword("wrong"); - - assertThrows(IOException.class, () -> { - SslSupport.createJdkSslContext(options); - }); - } - - @Test - public void testCreateSslContextWrongKeyStorePasswordOpenSSL() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - - SslOptions options = createJksSslOptions(); - options.keyStorePassword("wrong"); - - assertThrows(IOException.class, () -> { - SslSupport.createOpenSslContext(options); - }); - } - - @Test - public void testCreateSslContextBadPathToKeyStoreJDK() throws Exception { - SslOptions options = createJksSslOptions(); - options.keyStoreLocation(CLIENT_JKS_KEYSTORE + ".bad"); - - assertThrows(IOException.class, () -> { - SslSupport.createJdkSslContext(options); - }); - } - - @Test - public void testCreateSslContextBadPathToKeyStoreOpenSSL() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - - SslOptions options = createJksSslOptions(); - options.keyStoreLocation(CLIENT_JKS_KEYSTORE + ".bad"); - - assertThrows(IOException.class, () -> { - SslSupport.createOpenSslContext(options); - }); - } - - @Test - public void testCreateSslContextWrongTrustStorePasswordJDK() throws Exception { - SslOptions options = createJksSslOptions(); - options.trustStorePassword("wrong"); - - assertThrows(IOException.class, () -> { - SslSupport.createJdkSslContext(options); - }); - } - - @Test - public void testCreateSslContextWrongTrustStorePasswordOpenSSL() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - - SslOptions options = createJksSslOptions(); - options.trustStorePassword("wrong"); - - assertThrows(IOException.class, () -> { - SslSupport.createOpenSslContext(options); - }); - } - - @Test - public void testCreateSslContextBadPathToTrustStoreJDK() throws Exception { - SslOptions options = createJksSslOptions(); - options.trustStoreLocation(CLIENT_JKS_TRUSTSTORE + ".bad"); - - assertThrows(IOException.class, () -> { - SslSupport.createJdkSslContext(options); - }); - } - - @Test - public void testCreateSslContextBadPathToTrustStoreOpenSSL() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - - SslOptions options = createJksSslOptions(); - options.trustStoreLocation(CLIENT_JKS_TRUSTSTORE + ".bad"); - - assertThrows(IOException.class, () -> { - SslSupport.createOpenSslContext(options); - }); - } - - @Test - public void testCreateSslContextJceksStoreJDK() throws Exception { - SslOptions options = createJceksSslOptions(); - - SSLContext context = SslSupport.createJdkSslContext(options); - assertNotNull(context); - - assertEquals("TLS", context.getProtocol()); - } - - @Test - public void testCreateSslContextJceksStoreOpenSSL() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - - SslOptions options = createJceksSslOptions(); - - SslContext context = SslSupport.createOpenSslContext(options); - assertNotNull(context); - assertTrue(context.isClient()); - } - - @Test - public void testCreateSslContextPkcs12StoreJDK() throws Exception { - SslOptions options = createPkcs12SslOptions(); - - SSLContext context = SslSupport.createJdkSslContext(options); - assertNotNull(context); - - assertEquals("TLS", context.getProtocol()); - } - - @Test - public void testCreateSslContextPkcs12StoreOpenSSL() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - - SslOptions options = createPkcs12SslOptions(); - - SslContext context = SslSupport.createOpenSslContext(options); - assertNotNull(context); - assertTrue(context.isClient()); - } - - @Test - public void testCreateSslContextIncorrectStoreTypeJDK() throws Exception { - SslOptions options = createPkcs12SslOptions(); - options.storeType(KEYSTORE_JCEKS_TYPE); - - assertThrows(IOException.class, () -> { - SslSupport.createJdkSslContext(options); - }); - } - - @Test - public void testCreateSslContextIncorrectStoreTypeOpenSSL() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - - SslOptions options = createPkcs12SslOptions(); - options.storeType(KEYSTORE_JCEKS_TYPE); - - assertThrows(IOException.class, () -> { - SslSupport.createOpenSslContext(options); - }); - } - - @Test - public void testCreateSslEngineFromPkcs12StoreJDK() throws Exception { - SslOptions options = createPkcs12SslOptions(); - - SSLContext context = SslSupport.createJdkSslContext(options); - assertNotNull(context); - - SSLEngine engine = SslSupport.createJdkSslEngine(null, -1, context, options); - assertNotNull(engine); - - List engineProtocols = Arrays.asList(engine.getEnabledProtocols()); - assertFalse(engineProtocols.isEmpty()); - } - - @Test - public void testCreateSslEngineFromPkcs12StoreOpenSSL() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - - SslOptions options = createPkcs12SslOptions(); - - SslContext context = SslSupport.createOpenSslContext(options); - assertNotNull(context); - - SSLEngine engine = SslSupport.createOpenSslEngine(BufferAllocator.onHeapUnpooled(), null, -1, context, options); - assertNotNull(engine); - - List engineProtocols = Arrays.asList(engine.getEnabledProtocols()); - assertFalse(engineProtocols.isEmpty()); - } - - @Test - public void testCreateSslEngineFromPkcs12StoreWithExplicitEnabledProtocolsJDK() throws Exception { - SslOptions options = createPkcs12SslOptions(ENABLED_PROTOCOLS); - - SSLContext context = SslSupport.createJdkSslContext(options); - assertNotNull(context); - - SSLEngine engine = SslSupport.createJdkSslEngine(null, -1, context, options); - assertNotNull(engine); - - assertArrayEquals(ENABLED_PROTOCOLS, engine.getEnabledProtocols(), "Enabled protocols not as expected"); - } - - @Test - public void testCreateSslEngineFromPkcs12StoreWithExplicitEnabledProtocolsOpenSSL() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - - SslOptions options = createPkcs12SslOptions(ENABLED_PROTOCOLS); - - SslContext context = SslSupport.createOpenSslContext(options); - assertNotNull(context); - - SSLEngine engine = SslSupport.createOpenSslEngine(BufferAllocator.onHeapUnpooled(), null, -1, context, options); - assertNotNull(engine); - - assertArrayEquals(ENABLED_OPENSSL_PROTOCOLS, engine.getEnabledProtocols(), "Enabled protocols not as expected"); - } - - @Test - public void testCreateSslEngineFromJksStoreJDK() throws Exception { - SslOptions options = createJksSslOptions(); - - SSLContext context = SslSupport.createJdkSslContext(options); - assertNotNull(context); - - SSLEngine engine = SslSupport.createJdkSslEngine(null, -1, context, options); - assertNotNull(engine); - - List engineProtocols = Arrays.asList(engine.getEnabledProtocols()); - assertFalse(engineProtocols.isEmpty()); - } - - @Test - public void testCreateSslEngineFromJksStoreOpenSSL() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - - SslOptions options = createJksSslOptions(); - - SslContext context = SslSupport.createOpenSslContext(options); - assertNotNull(context); - - SSLEngine engine = SslSupport.createOpenSslEngine(BufferAllocator.onHeapUnpooled(), null, -1, context, options); - assertNotNull(engine); - - List engineProtocols = Arrays.asList(engine.getEnabledProtocols()); - assertFalse(engineProtocols.isEmpty()); - } - - @Test - public void testCreateSslEngineFromJksStoreWithExplicitEnabledProtocolsJDK() throws Exception { - SslOptions options = createJksSslOptions(ENABLED_PROTOCOLS); - - SSLContext context = SslSupport.createJdkSslContext(options); - assertNotNull(context); - - SSLEngine engine = SslSupport.createJdkSslEngine(null, -1, context, options); - assertNotNull(engine); - - assertArrayEquals(ENABLED_PROTOCOLS, engine.getEnabledProtocols(), "Enabled protocols not as expected"); - } - - @Test - public void testCreateSslEngineFromJksStoreWithExplicitEnabledProtocolsOpenSSL() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - - SslOptions options = createJksSslOptions(ENABLED_PROTOCOLS); - - SslContext context = SslSupport.createOpenSslContext(options); - assertNotNull(context); - - SSLEngine engine = SslSupport.createOpenSslEngine(BufferAllocator.onHeapUnpooled(), null, -1, context, options); - assertNotNull(engine); - - assertArrayEquals(ENABLED_OPENSSL_PROTOCOLS, engine.getEnabledProtocols(), "Enabled protocols not as expected"); - } - - @Test - public void testCreateSslEngineFromJksStoreWithExplicitDisabledProtocolsJDK() throws Exception { - // Discover the default enabled protocols - SslOptions options = createJksSslOptions(); - SSLEngine directEngine = createSSLEngineDirectly(options); - String[] protocols = directEngine.getEnabledProtocols(); - assertTrue(protocols.length > 0, "There were no initial protocols to choose from!"); - - // Pull out one to disable specifically - String[] disabledProtocol = new String[] { protocols[protocols.length - 1] }; - String[] trimmedProtocols = Arrays.copyOf(protocols, protocols.length - 1); - options.disabledProtocols(disabledProtocol); - SSLContext context = SslSupport.createJdkSslContext(options); - SSLEngine engine = SslSupport.createJdkSslEngine(null, -1, context, options); - - // verify the option took effect - assertNotNull(engine); - assertArrayEquals(trimmedProtocols, engine.getEnabledProtocols(), "Enabled protocols not as expected"); - } - - @Test - public void testCreateSslEngineFromJksStoreWithExplicitDisabledProtocolsOpenSSL() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - - // Discover the default enabled protocols - SslOptions options = createJksSslOptions(); - SSLEngine directEngine = createOpenSSLEngineDirectly(options); - String[] protocols = directEngine.getEnabledProtocols(); - assertTrue(protocols.length > 0, "There were no initial protocols to choose from!"); - - // Pull out one to disable specifically - String[] disabledProtocol = new String[] { protocols[protocols.length - 1] }; - String[] trimmedProtocols = Arrays.copyOf(protocols, protocols.length - 1); - options.disabledProtocols(disabledProtocol); - SslContext context = SslSupport.createOpenSslContext(options); - SSLEngine engine = SslSupport.createOpenSslEngine(BufferAllocator.onHeapUnpooled(), null, -1, context, options); - - // verify the option took effect - assertNotNull(engine); - assertArrayEquals(trimmedProtocols, engine.getEnabledProtocols(), "Enabled protocols not as expected"); - } - - @Test - public void testCreateSslEngineFromJksStoreWithExplicitEnabledAndDisabledProtocolsJDK() throws Exception { - // Discover the default enabled protocols - SslOptions options = createJksSslOptions(); - SSLEngine directEngine = createSSLEngineDirectly(options); - String[] protocols = directEngine.getEnabledProtocols(); - assertTrue(protocols.length > 1, "There were no initial protocols to choose from!"); - - // Pull out two to enable, and one to disable specifically - String protocol1 = protocols[0]; - String protocol2 = protocols[1]; - String[] enabledProtocols = new String[] { protocol1, protocol2 }; - String[] disabledProtocol = new String[] { protocol1 }; - String[] remainingProtocols = new String[] { protocol2 }; - options.enabledProtocols(enabledProtocols); - options.disabledProtocols(disabledProtocol); - SSLContext context = SslSupport.createJdkSslContext(options); - SSLEngine engine = SslSupport.createJdkSslEngine(null, -1, context, options); - - // verify the option took effect, that the disabled protocols were removed from the enabled list. - assertNotNull(engine); - assertArrayEquals(remainingProtocols, engine.getEnabledProtocols(), "Enabled protocols not as expected"); - } - - @Test - public void testCreateSslEngineFromJksStoreWithExplicitEnabledAndDisabledProtocolsOpenSSL() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - - // Discover the default enabled protocols - SslOptions options = createJksSslOptions(); - SSLEngine directEngine = createOpenSSLEngineDirectly(options); - String[] protocols = directEngine.getEnabledProtocols(); - assertTrue(protocols.length > 1, "There were no initial protocols to choose from!"); - - // Pull out two to enable, and one to disable specifically - String protocol1 = protocols[0]; - String protocol2 = protocols[1]; - String[] enabledProtocols = new String[] { protocol1, protocol2 }; - String[] disabledProtocol = new String[] { protocol1 }; - String[] remainingProtocols = new String[] { protocol2 }; - options.enabledProtocols(enabledProtocols); - options.disabledProtocols(disabledProtocol); - SslContext context = SslSupport.createOpenSslContext(options); - SSLEngine engine = SslSupport.createOpenSslEngine(BufferAllocator.onHeapUnpooled(), null, -1, context, options); - - // Because Netty cannot currently disable SSLv2Hello in OpenSSL we need to account for it popping up. - ArrayList remainingProtocolsList = new ArrayList<>(Arrays.asList(remainingProtocols)); - if (!remainingProtocolsList.contains("SSLv2Hello")) { - remainingProtocolsList.add(0, "SSLv2Hello"); - } - - remainingProtocols = remainingProtocolsList.toArray(new String[remainingProtocolsList.size()]); - - // verify the option took effect, that the disabled protocols were removed from the enabled list. - assertNotNull(engine); - assertEquals(remainingProtocolsList.size(), engine.getEnabledProtocols().length, "Enabled protocols not as expected"); - assertTrue(remainingProtocolsList.containsAll(Arrays.asList(engine.getEnabledProtocols())), "Enabled protocols not as expected"); - } - - @Test - public void testCreateSslEngineFromJksStoreWithExplicitEnabledCiphersJDK() throws Exception { - // Discover the default enabled ciphers - SslOptions options = createJksSslOptions(); - SSLEngine directEngine = createSSLEngineDirectly(options); - String[] ciphers = directEngine.getEnabledCipherSuites(); - assertTrue(ciphers.length > 0, "There were no initial ciphers to choose from!"); - - // Pull out one to enable specifically - String cipher = ciphers[0]; - String[] enabledCipher = new String[] { cipher }; - options.enabledCipherSuites(enabledCipher); - SSLContext context = SslSupport.createJdkSslContext(options); - SSLEngine engine = SslSupport.createJdkSslEngine(null, -1, context, options); - - // verify the option took effect - assertNotNull(engine); - assertArrayEquals(enabledCipher, engine.getEnabledCipherSuites(), "Enabled ciphers not as expected"); - } - - @Test - public void testCreateSslEngineFromJksStoreWithExplicitEnabledCiphersOpenSSL() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - - // Discover the default enabled ciphers - SslOptions options = createJksSslOptions(); - SSLEngine directEngine = createOpenSSLEngineDirectly(options); - String[] ciphers = directEngine.getEnabledCipherSuites(); - assertTrue(ciphers.length > 0, "There were no initial ciphers to choose from!"); - - // Pull out one to enable specifically - String cipher = ciphers[0]; - String[] enabledCipher = new String[] { cipher }; - options.enabledCipherSuites(enabledCipher); - SslContext context = SslSupport.createOpenSslContext(options); - SSLEngine engine = SslSupport.createOpenSslEngine(BufferAllocator.onHeapUnpooled(), null, -1, context, options); - - // verify the option took effect - assertNotNull(engine); - assertArrayEquals(enabledCipher, engine.getEnabledCipherSuites(), "Enabled ciphers not as expected"); - } - - @Test - public void testCreateSslEngineFromJksStoreWithExplicitDisabledCiphersJDK() throws Exception { - // Discover the default enabled ciphers - SslOptions options = createJksSslOptions(); - SSLEngine directEngine = createSSLEngineDirectly(options); - String[] ciphers = directEngine.getEnabledCipherSuites(); - assertTrue(ciphers.length > 0, "There were no initial ciphers to choose from!"); - - // Pull out one to disable specifically - String[] disabledCipher = new String[] { ciphers[ciphers.length - 1] }; - String[] trimmedCiphers = Arrays.copyOf(ciphers, ciphers.length - 1); - options.disabledCipherSuites(disabledCipher); - SSLContext context = SslSupport.createJdkSslContext(options); - SSLEngine engine = SslSupport.createJdkSslEngine(null, -1, context, options); - - // verify the option took effect - assertNotNull(engine); - assertArrayEquals(trimmedCiphers, engine.getEnabledCipherSuites(), "Enabled ciphers not as expected"); - } - - @Test - public void testCreateSslEngineFromJksStoreWithExplicitDisabledCiphersOpenSSL() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - - // Discover the default enabled ciphers - SslOptions options = createJksSslOptions(); - SSLEngine directEngine = createOpenSSLEngineDirectly(options); - String[] ciphers = directEngine.getEnabledCipherSuites(); - assertTrue(ciphers.length > 0, "There were no initial ciphers to choose from!"); - - // Pull out one to disable specifically - String[] disabledCipher = new String[] { ciphers[ciphers.length - 1] }; - String[] trimmedCiphers = Arrays.copyOf(ciphers, ciphers.length - 1); - options.disabledCipherSuites(disabledCipher); - SslContext context = SslSupport.createOpenSslContext(options); - SSLEngine engine = SslSupport.createOpenSslEngine(BufferAllocator.onHeapUnpooled(), null, -1, context, options); - - // verify the option took effect - assertNotNull(engine); - assertArrayEquals(trimmedCiphers, engine.getEnabledCipherSuites(), "Enabled ciphers not as expected"); - } - - @Test - public void testCreateSslEngineFromJksStoreWithExplicitEnabledAndDisabledCiphersJDK() throws Exception { - // Discover the default enabled ciphers - SslOptions options = createJksSslOptions(); - SSLEngine directEngine = createSSLEngineDirectly(options); - String[] ciphers = directEngine.getEnabledCipherSuites(); - assertTrue(ciphers.length > 1, "There werent enough initial ciphers to choose from!"); - - // Pull out two to enable, and one to disable specifically - String cipher1 = ciphers[0]; - String cipher2 = ciphers[1]; - String[] enabledCiphers = new String[] { cipher1, cipher2 }; - String[] disabledCipher = new String[] { cipher1 }; - String[] remainingCipher = new String[] { cipher2 }; - options.enabledCipherSuites(enabledCiphers); - options.disabledCipherSuites(disabledCipher); - SSLContext context = SslSupport.createJdkSslContext(options); - SSLEngine engine = SslSupport.createJdkSslEngine(null, -1, context, options); - - // verify the option took effect, that the disabled ciphers were removed from the enabled list. - assertNotNull(engine); - assertArrayEquals(remainingCipher, engine.getEnabledCipherSuites(), "Enabled ciphers not as expected"); - } - - @Test - public void testCreateSslEngineFromJksStoreWithExplicitEnabledAndDisabledCiphersOpenSSL() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - - // Discover the default enabled ciphers - SslOptions options = createJksSslOptions(); - SSLEngine directEngine = createOpenSSLEngineDirectly(options); - String[] ciphers = directEngine.getEnabledCipherSuites(); - assertTrue(ciphers.length > 1, "There werent enough initial ciphers to choose from!"); - - // Pull out two to enable, and one to disable specifically - String cipher1 = ciphers[0]; - String cipher2 = ciphers[1]; - String[] enabledCiphers = new String[] { cipher1, cipher2 }; - String[] disabledCipher = new String[] { cipher1 }; - String[] remainingCipher = new String[] { cipher2 }; - options.enabledCipherSuites(enabledCiphers); - options.disabledCipherSuites(disabledCipher); - SslContext context = SslSupport.createOpenSslContext(options); - SSLEngine engine = SslSupport.createOpenSslEngine(BufferAllocator.onHeapUnpooled(), null, -1, context, options); - - // verify the option took effect, that the disabled ciphers were removed from the enabled list. - assertNotNull(engine); - assertArrayEquals(remainingCipher, engine.getEnabledCipherSuites(), "Enabled ciphers not as expected"); - } - - @Test - public void testCreateSslEngineFromJceksStoreJDK() throws Exception { - SslOptions options = createJceksSslOptions(); - - SSLContext context = SslSupport.createJdkSslContext(options); - assertNotNull(context); - - SSLEngine engine = SslSupport.createJdkSslEngine(null, -1, context, options); - assertNotNull(engine); - - List engineProtocols = Arrays.asList(engine.getEnabledProtocols()); - assertFalse(engineProtocols.isEmpty()); - } - - @Test - public void testCreateSslEngineFromJceksStoreOpenSSL() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - - SslOptions options = createJceksSslOptions(); - - SslContext context = SslSupport.createOpenSslContext(options); - assertNotNull(context); - - SSLEngine engine = SslSupport.createOpenSslEngine(BufferAllocator.onHeapUnpooled(), null, -1, context, options); - assertNotNull(engine); - - List engineProtocols = Arrays.asList(engine.getEnabledProtocols()); - assertFalse(engineProtocols.isEmpty()); - } - - @Test - public void testCreateSslEngineFromJceksStoreWithExplicitEnabledProtocolsJDK() throws Exception { - SslOptions options = createJceksSslOptions(ENABLED_PROTOCOLS); - - SSLContext context = SslSupport.createJdkSslContext(options); - assertNotNull(context); - - SSLEngine engine = SslSupport.createJdkSslEngine(null, -1, context, options); - assertNotNull(engine); - - assertArrayEquals(ENABLED_PROTOCOLS, engine.getEnabledProtocols(), "Enabled protocols not as expected"); - } - - @Test - public void testCreateSslEngineFromJceksStoreWithExplicitEnabledProtocolsOpenSSL() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - - // Try and disable all but the one we really want but for now expect that this one plus SSLv2Hello - // is going to come back until the netty code can successfully disable them all. - SslOptions options = createJceksSslOptions(ENABLED_PROTOCOLS); - - SslContext context = SslSupport.createOpenSslContext(options); - assertNotNull(context); - - SSLEngine engine = SslSupport.createOpenSslEngine(BufferAllocator.onHeapUnpooled(), null, -1, context, options); - assertNotNull(engine); - - assertArrayEquals(ENABLED_OPENSSL_PROTOCOLS, engine.getEnabledProtocols(), "Enabled protocols not as expected"); - } - - @Test - public void testCreateSslEngineWithVerifyHostJDK() throws Exception { - SslOptions options = createJksSslOptions(); - options.verifyHost(true); - - SSLContext context = SslSupport.createJdkSslContext(options); - assertNotNull(context); - - SSLEngine engine = SslSupport.createJdkSslEngine(null, -1, context, options); - assertNotNull(engine); - - assertEquals("HTTPS", engine.getSSLParameters().getEndpointIdentificationAlgorithm()); - } - - @Test - public void testCreateSslEngineWithVerifyHostOpenSSL() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - assumeTrue(OpenSsl.supportsHostnameValidation()); - - SslOptions options = createJksSslOptions(); - options.verifyHost(true); - - SslContext context = SslSupport.createOpenSslContext(options); - assertNotNull(context); - - SSLEngine engine = SslSupport.createOpenSslEngine(BufferAllocator.onHeapUnpooled(), null, -1, context, options); - assertNotNull(engine); - - assertEquals("HTTPS", engine.getSSLParameters().getEndpointIdentificationAlgorithm()); - } - - @Test - public void testCreateSslEngineWithoutVerifyHostJDK() throws Exception { - SslOptions options = createJksSslOptions(); - options.verifyHost(false); - - SSLContext context = SslSupport.createJdkSslContext(options); - assertNotNull(context); - - SSLEngine engine = SslSupport.createJdkSslEngine(null, -1, context, options); - assertNotNull(engine); - - assertNull(engine.getSSLParameters().getEndpointIdentificationAlgorithm()); - } - - @Test - public void testCreateSslEngineWithoutVerifyHostOpenSSL() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - assumeTrue(OpenSsl.supportsHostnameValidation()); - - SslOptions options = createJksSslOptions(); - options.verifyHost(false); - - SslContext context = SslSupport.createOpenSslContext(options); - assertNotNull(context); - - SSLEngine engine = SslSupport.createOpenSslEngine(BufferAllocator.onHeapUnpooled(), null, -1, context, options); - assertNotNull(engine); - - assertNull(engine.getSSLParameters().getEndpointIdentificationAlgorithm()); - } - - @Test - public void testCreateSslContextWithKeyAliasWhichDoesntExist() throws Exception { - SslOptions options = createJksSslOptions(); - options.keyAlias(ALIAS_DOES_NOT_EXIST); - - try { - SslSupport.createJdkSslContext(options); - fail("Expected exception to be thrown"); - } catch (IllegalArgumentException iae) { - // Expected - } - } - - @Test - public void testCreateSslContextWithKeyAliasWhichRepresentsNonKeyEntry() throws Exception { - SslOptions options = createJksSslOptions(); - options.keyAlias(ALIAS_CA_CERT); - - try { - SslSupport.createJdkSslContext(options); - fail("Expected exception to be thrown"); - } catch (IllegalArgumentException iae) { - // Expected - } - } - - @Test - public void testIsOpenSSLPossible() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - - SslOptions options = new SslOptions(); - options.allowNativeSSL(false); - assertFalse(SslSupport.isOpenSSLPossible(options)); - - options.allowNativeSSL(true); - assertTrue(SslSupport.isOpenSSLPossible(options)); - } - - @Test - public void testIsOpenSSLPossibleWhenHostNameVerificationConfigured() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - assumeTrue(OpenSsl.supportsHostnameValidation()); - - SslOptions options = new SslOptions(); - options.allowNativeSSL(true); - - options.verifyHost(false); - assertTrue(SslSupport.isOpenSSLPossible(options)); - - options.verifyHost(true); - assertTrue(SslSupport.isOpenSSLPossible(options)); - } - - @Test - public void testIsOpenSSLPossibleWhenKeyAliasIsSpecified() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - assumeTrue(OpenSsl.supportsHostnameValidation()); - - SslOptions options = new SslOptions(); - options.allowNativeSSL(true); - options.keyAlias("alias"); - - assertFalse(SslSupport.isOpenSSLPossible(options)); - } - - @Test - public void testCreateSslHandlerJDK() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - - SslOptions options = new SslOptions(); - options.sslEnabled(true); - options.allowNativeSSL(false); - - SslHandler handler = SslSupport.createSslHandler(null, null, -1, options); - assertNotNull(handler); - assertFalse(handler.engine() instanceof OpenSslEngine); - } - - @Test - public void testCreateSslHandlerOpenSSL() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - - SslOptions options = new SslOptions(); - options.allowNativeSSL(true); - - SslHandler handler = SslSupport.createSslHandler(BufferAllocator.onHeapUnpooled(), null, -1, options); - assertNotNull(handler); - assertTrue(handler.engine() instanceof OpenSslEngine); - } - - @Test - public void testCreateOpenSSLEngineFailsWhenAllocatorMissing() throws Exception { - assumeTrue(OpenSsl.isAvailable()); - assumeTrue(OpenSsl.supportsKeyManagerFactory()); - - SslOptions options = new SslOptions(); - options.allowNativeSSL(true); - - SslContext context = SslSupport.createOpenSslContext(options); - try { - SslSupport.createOpenSslEngine(null, null, -1, context, options); - fail("Should throw IllegalArgumentException for null allocator."); - } catch (IllegalArgumentException iae) {} - } - - private SslOptions createJksSslOptions() { - return createJksSslOptions(null); - } - - private SslOptions createJksSslOptions(String[] enabledProtocols) { - SslOptions options = new SslOptions(); - - options.sslEnabled(true); - options.keyStoreLocation(CLIENT_JKS_KEYSTORE); - options.trustStoreLocation(CLIENT_JKS_TRUSTSTORE); - options.storeType(KEYSTORE_JKS_TYPE); - options.keyStorePassword(PASSWORD); - options.trustStorePassword(PASSWORD); - if (enabledProtocols != null) { - options.enabledProtocols(enabledProtocols); - } - - return options; - } - - private SslOptions createJceksSslOptions() { - return createJceksSslOptions(null); - } - - private SslOptions createJceksSslOptions(String[] enabledProtocols) { - SslOptions options = new SslOptions(); - - options.sslEnabled(true); - options.keyStoreLocation(CLIENT_JCEKS_KEYSTORE); - options.trustStoreLocation(CLIENT_JCEKS_TRUSTSTORE); - options.storeType(KEYSTORE_JCEKS_TYPE); - options.keyStorePassword(PASSWORD); - options.trustStorePassword(PASSWORD); - if (enabledProtocols != null) { - options.enabledProtocols(enabledProtocols); - } - - return options; - } - - private SslOptions createPkcs12SslOptions() { - return createPkcs12SslOptions(null); - } - - private SslOptions createPkcs12SslOptions(String[] enabledProtocols) { - SslOptions options = new SslOptions(); - - options.keyStoreLocation(CLIENT_PKCS12_KEYSTORE); - options.trustStoreLocation(CLIENT_PKCS12_TRUSTSTORE); - options.storeType(KEYSTORE_PKCS12_TYPE); - options.keyStorePassword(PASSWORD); - options.trustStorePassword(PASSWORD); - if (enabledProtocols != null) { - options.enabledProtocols(enabledProtocols); - } - - return options; - } - - private SSLEngine createSSLEngineDirectly(SslOptions options) throws Exception { - SSLContext context = SslSupport.createJdkSslContext(options); - SSLEngine engine = context.createSSLEngine(); - return engine; - } - - private SSLEngine createOpenSSLEngineDirectly(SslOptions options) throws Exception { - SslContext context = SslSupport.createOpenSslContext(options); - SSLEngine engine = context.newEngine(BufferAllocator.onHeapUnpooled()); - return engine; - } -} diff --git a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/SslTransportTest.java b/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/SslTransportTest.java deleted file mode 100644 index 6083305f..00000000 --- a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/SslTransportTest.java +++ /dev/null @@ -1,435 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.qpid.protonj2.client.transport.netty5; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -import java.io.IOException; -import java.net.URISyntaxException; -import java.security.cert.Certificate; -import java.security.cert.X509Certificate; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -import org.apache.qpid.protonj2.client.SslOptions; -import org.apache.qpid.protonj2.client.transport.Transport; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * Test basic functionality of the Netty based TCP Transport running in secure mode (SSL). - */ -@Timeout(30) -public class SslTransportTest extends TcpTransportTest { - - private static final Logger LOG = LoggerFactory.getLogger(SslTransportTest.class); - - public static final String PASSWORD = "password"; - public static final String SERVER_KEYSTORE = "src/test/resources/broker-jks.keystore"; - public static final String SERVER_TRUSTSTORE = "src/test/resources/broker-jks.truststore"; - public static final String SERVER_WRONG_HOST_KEYSTORE = "src/test/resources/broker-wrong-host-jks.keystore"; - public static final String CLIENT_KEYSTORE = "src/test/resources/client-jks.keystore"; - public static final String CLIENT_MULTI_KEYSTORE = "src/test/resources/client-multiple-keys-jks.keystore"; - public static final String CLIENT_TRUSTSTORE = "src/test/resources/client-jks.truststore"; - public static final String OTHER_CA_TRUSTSTORE = "src/test/resources/other-ca-jks.truststore"; - public static final String SERVER_CLASSPATH_KEYSTORE = "classpath:broker-jks.keystore"; - public static final String SERVER_CLASSPATH_TRUSTSTORE = "classpath:broker-jks.truststore"; - - public static final String CLIENT_KEY_ALIAS = "client"; - public static final String CLIENT_DN = "O=Client,CN=client"; - public static final String CLIENT2_KEY_ALIAS = "client2"; - public static final String CLIENT2_DN = "O=Client2,CN=client2"; - - public static final String KEYSTORE_TYPE = "jks"; - - @Test - public void testConnectToServerWithoutTrustStoreFails() throws Exception { - try (NettyEchoServer server = createEchoServer()) { - server.start(); - - final int port = server.getServerPort(); - - Transport transport = createTransport(createTransportOptions(), createSSLOptionsWithoutTrustStore(false)); - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - fail("Should have failed to connect to the server: " + HOSTNAME + ":" + port); - } catch (Exception e) { - LOG.info("Connection failed to untrusted test server: {}:{}", HOSTNAME, port); - } - - assertFalse(transport.isConnected()); - - transport.close(); - } - - logTransportErrors(); - - assertFalse(exceptions.isEmpty()); - } - - @Test - public void testConnectToServerUsingUntrustedKeyFails() throws Exception { - try (NettyEchoServer server = createEchoServer()) { - server.start(); - - final int port = server.getServerPort(); - - SslOptions sslOptions = createSSLOptions(); - - sslOptions.trustStoreLocation(OTHER_CA_TRUSTSTORE); - sslOptions.trustStorePassword(PASSWORD); - - Transport transport = createTransport(createTransportOptions(), sslOptions); - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - fail("Should have failed to connect to the server: " + HOSTNAME + ":" + port); - } catch (Exception e) { - LOG.info("Connection failed to untrusted test server: {}:{}", HOSTNAME, port); - } - - assertFalse(transport.isConnected()); - - transport.close(); - } - } - - @Test - public void testConnectToServerWithWrongKeyStorePasswordFails() throws Exception { - try (NettyEchoServer server = createEchoServer()) { - server.start(); - - final int port = server.getServerPort(); - final CountDownLatch errored = new CountDownLatch(1); - final SslOptions sslOptions = createSSLOptions(); - - sslOptions.keyStoreLocation(CLIENT_KEYSTORE); - sslOptions.keyStorePassword("wrong"); - - Transport transport = createTransport(createTransportOptions(), sslOptions); - transport.connect(HOSTNAME, port, new NettyTransportListener() { - - @Override - public void transportError(Throwable cause) { - LOG.info("Transport error caught: {}", cause.getMessage(), cause); - errored.countDown(); - } - }); - - assertTrue(errored.await(5, TimeUnit.SECONDS)); - - try { - transport.awaitConnect(); - fail("Should have failed to connect to the server: " + HOSTNAME + ":" + port); - } catch (Exception e) { - LOG.info("Connection failed when key store password was incorrect: {}:{}", HOSTNAME, port); - } - - assertFalse(transport.isConnected()); - - transport.close(); - } - } - - @Test - public void testConnectToServerWithWrongTrustStorePasswordFails() throws Exception { - try (NettyEchoServer server = createEchoServer()) { - server.start(); - - final int port = server.getServerPort(); - final CountDownLatch errored = new CountDownLatch(1); - final SslOptions sslOptions = createSSLOptions(); - - sslOptions.trustStoreLocation(CLIENT_TRUSTSTORE); - sslOptions.trustStorePassword("wrong"); - - Transport transport = createTransport(createTransportOptions(), sslOptions); - transport.connect(HOSTNAME, port, new NettyTransportListener() { - - @Override - public void transportError(Throwable cause) { - LOG.info("Transport error caught: {}", cause.getMessage(), cause); - errored.countDown(); - } - }); - - assertTrue(errored.await(5, TimeUnit.SECONDS)); - - try { - transport.awaitConnect(); - fail("Should have failed to connect to the server: " + HOSTNAME + ":" + port); - } catch (Exception e) { - LOG.info("Connection failed when trust store password was incorrect: {}:{}", HOSTNAME, port); - } - - assertFalse(transport.isConnected()); - - transport.close(); - } - } - - @Test - public void testConnectToServerClientTrustsAll() throws Exception { - try (NettyEchoServer server = createEchoServer()) { - server.start(); - - final int port = server.getServerPort(); - - Transport transport = createTransport(createTransportOptions(), createSSLOptionsWithoutTrustStore(true)); - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - LOG.info("Connection established to test server: {}:{}", HOSTNAME, port); - } catch (Exception e) { - fail("Should not have failed to connect to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - - assertTrue(transport.isConnected()); - assertTrue(transport.isSecure()); - - transport.close(); - } - - logTransportErrors(); - assertTrue(exceptions.isEmpty()); - } - - @Test - public void testConnectToServerWithServerClasspathStores() throws Exception { - try (NettyEchoServer server = createEchoServer()) { - server.start(); - - final int port = server.getServerPort(); - - Transport transport = createTransport(createTransportOptions(), createServerClasspathSSLOptions()); - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - LOG.info("Connection established to test server: {}:{}", HOSTNAME, port); - } catch (Exception e) { - fail("Should not have failed to connect to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - - assertTrue(transport.isConnected()); - assertTrue(transport.isSecure()); - - transport.close(); - } - - logTransportErrors(); - assertTrue(exceptions.isEmpty()); - } - - @Test - public void testConnectWithNeedClientAuth() throws Exception { - try (NettyEchoServer server = createEchoServer(true)) { - server.start(); - - final int port = server.getServerPort(); - - Transport transport = createTransport(createTransportOptions(), createSSLOptions()); - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - LOG.info("Connection established to test server: {}:{}", HOSTNAME, port); - } catch (Exception e) { - fail("Should not have failed to connect to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - - assertTrue(transport.isConnected()); - assertTrue(transport.isSecure()); - - // Verify there was a certificate sent to the server - server.getSslHandler().handshakeFuture().asStage().await(2, TimeUnit.SECONDS); - - assertTrue(server.getSslHandler().handshakeFuture().isSuccess(), "Server handshake did not complete in allotted time"); - assertNotNull(server.getSslHandler().engine().getSession().getPeerCertificates()); - - transport.close(); - } - - logTransportErrors(); - assertTrue(exceptions.isEmpty()); - } - - @Test - public void testConnectWithSpecificClientAuthKeyAlias1() throws Exception { - doClientAuthAliasTestImpl(CLIENT_KEY_ALIAS, CLIENT_DN); - } - - @Test - public void testConnectWithSpecificClientAuthKeyAlias2() throws Exception { - doClientAuthAliasTestImpl(CLIENT2_KEY_ALIAS, CLIENT2_DN); - } - - private void doClientAuthAliasTestImpl(String alias, String expectedDN) throws Exception, URISyntaxException, IOException, InterruptedException { - try (NettyEchoServer server = createEchoServer(true)) { - server.start(); - - final int port = server.getServerPort(); - - SslOptions sslOptions = createSSLOptions(); - sslOptions.keyStoreLocation(CLIENT_MULTI_KEYSTORE); - sslOptions.keyAlias(alias); - - Transport transport = createTransport(createTransportOptions(), sslOptions); - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - LOG.info("Connection established to test server: {}:{}", HOSTNAME, port); - } catch (Exception e) { - fail("Should not have failed to connect to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - - assertTrue(transport.isConnected()); - assertTrue(transport.isSecure()); - - server.getSslHandler().handshakeFuture().asStage().await(2, TimeUnit.SECONDS); - - assertTrue(server.getSslHandler().handshakeFuture().isSuccess(), "Server handshake did not complete in allotted time"); - - Certificate[] peerCertificates = server.getSslHandler().engine().getSession().getPeerCertificates(); - assertNotNull(peerCertificates); - - Certificate cert = peerCertificates[0]; - assertTrue(cert instanceof X509Certificate); - String dn = ((X509Certificate)cert).getSubjectX500Principal().getName(); - assertEquals(expectedDN, dn, "Unexpected certificate DN"); - - transport.close(); - } - - logTransportErrors(); - assertTrue(exceptions.isEmpty()); - } - - @Test - public void testConnectToServerVerifyHost() throws Exception { - doConnectToServerVerifyHostTestImpl(true); - } - - @Test - public void testConnectToServerNoVerifyHost() throws Exception { - doConnectToServerVerifyHostTestImpl(false); - } - - private void doConnectToServerVerifyHostTestImpl(boolean verifyHost) throws Exception, URISyntaxException, IOException, InterruptedException { - SslOptions serverOptions = createServerSSLOptions(); - serverOptions.keyStoreLocation(SERVER_WRONG_HOST_KEYSTORE); - - try (NettyEchoServer server = createEchoServer(serverOptions)) { - server.start(); - - final int port = server.getServerPort(); - - SslOptions clientOptions = createSSLOptionsIsVerify(verifyHost); - - if (verifyHost) { - assertTrue(clientOptions.verifyHost(), "Expected verifyHost to be true"); - } else { - assertFalse(clientOptions.verifyHost(), "Expected verifyHost to be false"); - } - - Transport transport = createTransport(createTransportOptions(), clientOptions); - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - if (verifyHost) { - fail("Should not have connected to the server: " + HOSTNAME + ":" + port); - } - } catch (Exception e) { - if (verifyHost) { - LOG.info("Connection failed to test server: {}:{} as expected.", HOSTNAME, port); - } else { - LOG.error("Failed to connect to test server: {}:{}" + HOSTNAME, port, e); - fail("Should have connected to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - } - - assertNotNull(transport.getSslOptions()); - assertEquals(verifyHost, transport.getSslOptions().verifyHost()); - - if (verifyHost) { - assertFalse(transport.isConnected()); - } else { - assertTrue(transport.isConnected()); - } - - transport.close(); - } - } - - @Override - protected SslOptions createSSLOptions() { - return createSSLOptionsIsVerify(false); - } - - protected SslOptions createSSLOptionsIsVerify(boolean verifyHost) { - SslOptions options = new SslOptions(); - - options.sslEnabled(true); - options.keyStoreLocation(CLIENT_KEYSTORE); - options.keyStorePassword(PASSWORD); - options.trustStoreLocation(CLIENT_TRUSTSTORE); - options.trustStorePassword(PASSWORD); - options.storeType(KEYSTORE_TYPE); - options.verifyHost(verifyHost); - - return options; - } - - protected SslOptions createSSLOptionsWithoutTrustStore(boolean trustAll) { - SslOptions options = new SslOptions(); - - options.sslEnabled(true); - options.storeType(KEYSTORE_TYPE); - options.trustAll(trustAll); - - return options; - } - - @Override - protected SslOptions createServerSSLOptions() { - SslOptions options = new SslOptions(); - - // Run the server in JDK mode for now to validate cross compatibility - options.sslEnabled(true); - options.keyStoreLocation(SERVER_KEYSTORE); - options.keyStorePassword(PASSWORD); - options.trustStoreLocation(SERVER_TRUSTSTORE); - options.trustStorePassword(PASSWORD); - options.storeType(KEYSTORE_TYPE); - options.verifyHost(false); - - return options; - } - - protected SslOptions createServerClasspathSSLOptions() { - SslOptions options = new SslOptions(); - - // Run the server in JDK mode for now to validate cross compatibility - options.sslEnabled(true); - options.keyStoreLocation(SERVER_CLASSPATH_KEYSTORE); - options.keyStorePassword(PASSWORD); - options.trustStoreLocation(SERVER_CLASSPATH_TRUSTSTORE); - options.trustStorePassword(PASSWORD); - options.storeType(KEYSTORE_TYPE); - options.verifyHost(false); - - return options; - } -} diff --git a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/TcpTransportTest.java b/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/TcpTransportTest.java deleted file mode 100644 index 3999bcb3..00000000 --- a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/TcpTransportTest.java +++ /dev/null @@ -1,1196 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.qpid.protonj2.client.transport.netty5; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; -import static org.junit.jupiter.api.Assumptions.assumeFalse; -import static org.junit.jupiter.api.Assumptions.assumeTrue; - -import java.io.IOException; -import java.lang.reflect.Field; -import java.net.URI; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicInteger; - -import org.apache.qpid.protonj2.buffer.ProtonBuffer; -import org.apache.qpid.protonj2.buffer.ProtonBufferAllocator; -import org.apache.qpid.protonj2.buffer.netty.Netty4ProtonBufferAllocator; -import org.apache.qpid.protonj2.client.SslOptions; -import org.apache.qpid.protonj2.client.TransportOptions; -import org.apache.qpid.protonj2.client.test.ImperativeClientTestCase; -import org.apache.qpid.protonj2.client.test.Wait; -import org.apache.qpid.protonj2.client.transport.Transport; -import org.apache.qpid.protonj2.client.transport.TransportListener; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.RepeatedTest; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInfo; -import org.junit.jupiter.api.Timeout; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.netty.buffer.UnpooledByteBufAllocator; -import io.netty.incubator.channel.uring.IOUring; -import io.netty5.bootstrap.Bootstrap; -import io.netty5.channel.IoHandler; -import io.netty5.channel.MultithreadEventLoopGroup; -import io.netty5.channel.SingleThreadEventLoop; -import io.netty5.channel.epoll.Epoll; -import io.netty5.channel.epoll.EpollHandler; -import io.netty5.channel.kqueue.KQueue; -import io.netty5.channel.kqueue.KQueueHandler; -import io.netty5.util.ResourceLeakDetector; -import io.netty5.util.ResourceLeakDetector.Level; - -/** - * Test basic functionality of the Netty based TCP transport. - */ -@Timeout(30) -public class TcpTransportTest extends ImperativeClientTestCase { - - private static final Logger LOG = LoggerFactory.getLogger(TcpTransportTest.class); - - protected static final int SEND_BYTE_COUNT = 1024; - protected static final String HOSTNAME = "localhost"; - - protected volatile boolean transportInitialized; - protected volatile boolean transportConnected; - protected volatile boolean transportErrored; - protected final List exceptions = new ArrayList<>(); - protected final List data = new ArrayList<>(); - protected final AtomicInteger bytesRead = new AtomicInteger(); - - protected final TransportListener testListener = new NettyTransportListener(); - - protected ProtonBufferAllocator allocator; - protected Netty5IOContext context; - - @Override - @BeforeEach - public void setUp(TestInfo testInfo) throws Exception { - super.setUp(testInfo); - - allocator = new Netty4ProtonBufferAllocator(UnpooledByteBufAllocator.DEFAULT); - } - - @Override - @AfterEach - public void tearDown(TestInfo testInfo) throws Exception { - super.tearDown(testInfo); - - if (context != null) { - context.shutdown(); - context = null; - } - - data.removeIf((buffer) -> { - buffer.close(); - return true; - }); - } - - @Test - public void testCannotCreateWithIllegalArgs() throws Exception { - assertThrows(IllegalArgumentException.class, () -> new TcpTransport(null, createTransportOptions(), createSSLOptions())); - assertThrows(IllegalArgumentException.class, () -> new TcpTransport(new Bootstrap(), null, createSSLOptions())); - assertThrows(IllegalArgumentException.class, () -> new TcpTransport(new Bootstrap(), createTransportOptions(), null)); - } - - @Test - public void testCloseOnNeverConnectedTransport() throws Exception { - Transport transport = createTransport(createTransportOptions(), createSSLOptions()); - assertFalse(transport.isConnected()); - - transport.close(); - - assertFalse(transportInitialized); - assertFalse(transportConnected); - assertFalse(transportErrored); - assertTrue(exceptions.isEmpty()); - assertTrue(data.isEmpty()); - } - - @Test - public void testCannotCallConnectOnClosedTransport() throws Exception { - Transport transport = createTransport(createTransportOptions(), createSSLOptions()); - - transport.close(); - - assertThrows(IllegalStateException.class, () -> transport.connect("localhost", 5672, testListener)); - } - - @Test - public void testCreateWithBadHostOrPortThrowsIAE() throws Exception { - Transport transport = createTransport(createTransportOptions().defaultTcpPort(-1), createSSLOptions().defaultSslPort(-1)); - - try { - transport.connect(HOSTNAME, -1, testListener); - fail("Should have thrown IllegalArgumentException"); - } catch (IllegalArgumentException iae) { - } - - try { - transport.connect(null, 5672, testListener); - fail("Should have thrown IllegalArgumentException"); - } catch (IllegalArgumentException iae) { - } - - try { - transport.connect("", 5672, testListener); - fail("Should have thrown IllegalArgumentException"); - } catch (IllegalArgumentException iae) { - } - } - - @Test - public void testCreateWithNullOptionsThrowsIAE() throws Exception { - try { - new Netty5IOContext(null, null, getTestName()); - fail("Should have thrown NullPointerException"); - } catch (NullPointerException npe) { - } - - try { - new Netty5IOContext(createTransportOptions(), null, getTestName()); - fail("Should have thrown NullPointerException"); - } catch (NullPointerException npe) { - } - - try { - new Netty5IOContext(null, createSSLOptions(), getTestName()); - fail("Should have thrown NullPointerException"); - } catch (NullPointerException npe) { - } - } - - @Test - public void testConnectWithoutRunningServer() throws Exception { - try (NettyEchoServer server = createEchoServer()) { - server.start(); - - final int port = server.getServerPort(); - - server.close(); - - Transport transport = createTransport(createTransportOptions(), createSSLOptions()); - - assertNull(transport.getTransportListener()); - - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - fail("Should have failed to connect to the server: " + HOSTNAME + ":" + port); - } catch (Exception e) { - LOG.info("Failed to connect to: {}:{} as expected.", HOSTNAME, port); - } - - assertEquals(testListener, transport.getTransportListener()); - assertFalse(transport.isConnected()); - - transport.close(); - } - - assertTrue(transportInitialized); - assertFalse(transportConnected); - assertTrue(transportErrored); - assertFalse(exceptions.isEmpty()); - assertTrue(data.isEmpty()); - } - - @Test - public void testConnectWithoutListenerFails() throws Exception { - try (NettyEchoServer server = createEchoServer()) { - server.start(); - - final int port = server.getServerPort(); - - Transport transport = createTransport(createTransportOptions(), createSSLOptions()); - - try { - transport.connect(HOSTNAME, port, null); - fail("Should have failed to connect to the server: " + HOSTNAME + ":" + port); - } catch (IllegalArgumentException e) { - LOG.info("Failed to connect to: {}:{} as expected.", HOSTNAME, port); - } - - assertFalse(transport.isConnected()); - - transport.close(); - } - } - - @Test - public void testConnectToServer() throws Exception { - try (NettyEchoServer server = createEchoServer()) { - server.start(); - - final int port = server.getServerPort(); - - Transport transport = createTransport(createTransportOptions(), createSSLOptions()); - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - LOG.info("Connected to server:{}:{} as expected.", HOSTNAME, port); - } catch (Exception e) { - fail("Should not have failed to connect to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - - assertTrue(transport.isConnected()); - assertEquals(HOSTNAME, transport.getHost(), "Server host is incorrect"); - assertEquals(port, transport.getPort(), "Server port is incorrect"); - - final URI remoteURI = transport.getRemoteURI(); - - if (transport.isSecure()) { - if (transport.getTransportOptions().useWebSockets()) { - assertEquals(remoteURI.getScheme(), "wss"); - } else { - assertEquals(remoteURI.getScheme(), "ssl"); - } - } else { - if (transport.getTransportOptions().useWebSockets()) { - assertEquals(remoteURI.getScheme(), "ws"); - } else { - assertEquals(remoteURI.getScheme(), "tcp"); - } - } - - transport.close(); - - // Additional close should not fail or cause other problems. - transport.close(); - } - - assertTrue(transportInitialized); - assertTrue(transportConnected); - assertFalse(transportErrored); // Normal shutdown does not trigger the event. - assertTrue(exceptions.isEmpty()); - assertTrue(data.isEmpty()); - } - - @Test - public void testMultipleConnectionsToServer() throws Exception { - final int CONNECTION_COUNT = 10; - - try (NettyEchoServer server = createEchoServer()) { - server.start(); - - final int port = server.getServerPort(); - - List transports = new ArrayList<>(); - - Netty5IOContext context = createContext(createTransportOptions(), createSSLOptions()); - - for (int i = 0; i < CONNECTION_COUNT; ++i) { - Transport transport = context.newTransport(); - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - assertTrue(transport.isConnected()); - LOG.info("Connected to server:{}:{} as expected.", HOSTNAME, port); - transports.add(transport); - } catch (Exception e) { - fail("Should not have failed to connect to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - } - - for (Transport transport : transports) { - transport.close(); - } - } - - assertTrue(transportInitialized); - assertFalse(transportErrored); // Normal shutdown does not trigger the event. - assertTrue(exceptions.isEmpty()); - assertTrue(data.isEmpty()); - } - - @Test - public void testMultipleConnectionsSendReceive() throws Exception { - final int CONNECTION_COUNT = 10; - final int FRAME_SIZE = 8; - - ProtonBuffer sendBuffer = allocator.allocate(FRAME_SIZE); - for (int i = 0; i < 8; ++i) { - sendBuffer.writeByte((byte) 'A'); - } - - try (NettyEchoServer server = createEchoServer()) { - server.start(); - - final int port = server.getServerPort(); - - List transports = new ArrayList<>(); - - Netty5IOContext context = createContext(createTransportOptions(), createSSLOptions()); - - for (int i = 0; i < CONNECTION_COUNT; ++i) { - Transport transport = context.newTransport(); - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - transport.writeAndFlush(sendBuffer.copy()); - transports.add(transport); - } catch (Exception e) { - fail("Should not have failed to connect to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - } - - assertTrue(Wait.waitFor(new Wait.Condition() { - @Override - public boolean isSatisfied() throws Exception { - LOG.debug("Checking completion: read {} expecting {}", bytesRead.get(), (FRAME_SIZE * CONNECTION_COUNT)); - return bytesRead.get() == (FRAME_SIZE * CONNECTION_COUNT); - } - }, 10000, 50)); - - for (Transport transport : transports) { - transport.close(); - } - } - - assertTrue(exceptions.isEmpty()); - } - - @RepeatedTest(1) - public void testDetectServerClose() throws Exception { - Transport transport = null; - - try (NettyEchoServer server = createEchoServer()) { - server.start(); - - int port = server.getServerPort(); - - transport = createTransport(createTransportOptions(), createSSLOptions()); - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - LOG.info("Connected to server:{}:{} as expected.", HOSTNAME, port); - } catch (Exception e) { - fail("Should not have failed to connect to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - - assertTrue(transport.isConnected()); - - assertTrue(Wait.waitFor(new Wait.Condition() { - @Override - public boolean isSatisfied() throws Exception { - return server.getClientCount() == 1; - } - }, 10000, 50)); - } - - final Transport connectedTransport = transport; - assertTrue(Wait.waitFor(new Wait.Condition() { - @Override - public boolean isSatisfied() throws Exception { - return !connectedTransport.isConnected(); - } - }, 10000, 50)); - - assertTrue(data.isEmpty()); - - try { - transport.close(); - } catch (Exception ex) { - fail("Close of a disconnect transport should not generate errors"); - } - } - - @Test - public void testZeroSizedSentNoErrorsWriteAndFlush() throws Exception { - testZeroSizedSentNoErrors(true); - } - - @Test - public void testZeroSizedSentNoErrorsWriteThenFlush() throws Exception { - testZeroSizedSentNoErrors(false); - } - - private void testZeroSizedSentNoErrors(boolean writeAndFlush) throws Exception { - try (NettyEchoServer server = createEchoServer()) { - server.start(); - - int port = server.getServerPort(); - - Transport transport = createTransport(createTransportOptions(), createSSLOptions()); - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - LOG.info("Connected to server:{}:{} as expected.", HOSTNAME, port); - } catch (Exception e) { - fail("Should not have failed to connect to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - - assertTrue(transport.isConnected()); - - final ProtonBuffer sendBuffer = allocator.allocate(0); - - if (writeAndFlush) { - transport.writeAndFlush(sendBuffer); - } else { - transport.write(sendBuffer); - transport.flush(); - } - - transport.close(); - } - - assertTrue(!transportErrored); // Normal shutdown does not trigger the event. - assertTrue(exceptions.isEmpty()); - assertTrue(data.isEmpty()); - } - - @Test - public void testUseAllocatorToCreateFixedSizeOutputBuffer() throws Exception { - try (NettyEchoServer server = createEchoServer()) { - server.start(); - - int port = server.getServerPort(); - - Transport transport = createTransport(createTransportOptions(), createSSLOptions()); - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - LOG.info("Connected to server:{}:{} as expected.", HOSTNAME, port); - } catch (Exception e) { - fail("Should not have failed to connect to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - - assertTrue(transport.isConnected()); - - ProtonBuffer buffer = transport.getBufferAllocator().allocate(64).implicitGrowthLimit(512); - - assertEquals(64, buffer.capacity()); - assertEquals(512, buffer.implicitGrowthLimit()); - - transport.writeAndFlush(buffer); - - transport.close(); - } - - assertTrue(!transportErrored); // Normal shutdown does not trigger the event. - assertTrue(exceptions.isEmpty()); - assertTrue(data.isEmpty()); - } - - @Test - public void testUseAllocatorToCreateFixedSizeHeapBuffer() throws Exception { - try (NettyEchoServer server = createEchoServer()) { - server.start(); - - int port = server.getServerPort(); - - Transport transport = createTransport(createTransportOptions(), createSSLOptions()); - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - LOG.info("Connected to server:{}:{} as expected.", HOSTNAME, port); - } catch (Exception e) { - fail("Should not have failed to connect to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - - assertTrue(transport.isConnected()); - - ProtonBuffer buffer = transport.getBufferAllocator().allocate(64).implicitGrowthLimit(512); - - assertEquals(64, buffer.capacity()); - assertEquals(512, buffer.implicitGrowthLimit()); - - transport.writeAndFlush(buffer); - - transport.close(); - } - - assertTrue(!transportErrored); // Normal shutdown does not trigger the event. - assertTrue(exceptions.isEmpty()); - assertTrue(data.isEmpty()); - } - - @Test - public void testDataSentWithWriteAndThenFlushedIsReceived() throws Exception { - try (NettyEchoServer server = createEchoServer()) { - server.start(); - - int port = server.getServerPort(); - - Transport transport = createTransport(createTransportOptions(), createSSLOptions()); - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - LOG.info("Connected to server:{}:{} as expected.", HOSTNAME, port); - } catch (Exception e) { - fail("Should not have failed to connect to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - - assertTrue(transport.isConnected()); - - ProtonBuffer sendBuffer = transport.getBufferAllocator().outputBuffer(SEND_BYTE_COUNT); - for (int i = 0; i < SEND_BYTE_COUNT; ++i) { - sendBuffer.writeByte((byte) 'A'); - } - - transport.write(sendBuffer, () -> LOG.debug("Netty reports write complete")); - LOG.trace("Flush of Transport happens now"); - transport.flush(); - - assertTrue(Wait.waitFor(new Wait.Condition() { - @Override - public boolean isSatisfied() throws Exception { - return !data.isEmpty(); - } - }, 10000, 50)); - - assertEquals(SEND_BYTE_COUNT, data.get(0).getReadableBytes()); - - transport.close(); - } - - assertTrue(!transportErrored); // Normal shutdown does not trigger the event. - assertTrue(exceptions.isEmpty()); - } - - @Test - public void testDataSentWithinCompositeBufferIsReceived() throws Exception { - try (NettyEchoServer server = createEchoServer()) { - server.start(); - - int port = server.getServerPort(); - - Transport transport = createTransport(createTransportOptions(), createSSLOptions()); - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - LOG.info("Connected to server:{}:{} as expected.", HOSTNAME, port); - } catch (Exception e) { - fail("Should not have failed to connect to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - - assertTrue(transport.isConnected()); - - ProtonBuffer sendBuffer1 = transport.getBufferAllocator().outputBuffer(SEND_BYTE_COUNT); - for (int i = 0; i < SEND_BYTE_COUNT; ++i) { - sendBuffer1.writeByte((byte) 'A'); - } - ProtonBuffer sendBuffer2 = transport.getBufferAllocator().outputBuffer(SEND_BYTE_COUNT + 10); - for (int i = 0; i < SEND_BYTE_COUNT + 10; ++i) { - sendBuffer2.writeByte((byte) 'A'); - } - - ProtonBuffer sendBuffer = transport.getBufferAllocator().composite(new ProtonBuffer[] { sendBuffer1, sendBuffer2}); - - transport.write(sendBuffer, () -> LOG.debug("Netty reports write complete")); - LOG.trace("Flush of Transport happens now"); - transport.flush(); - - sendBuffer.close(); - - assertTrue(Wait.waitFor(new Wait.Condition() { - @Override - public boolean isSatisfied() throws Exception { - return bytesRead.get() == SEND_BYTE_COUNT + SEND_BYTE_COUNT + 10; - } - }, 10000, 50)); - - transport.close(); - } - - assertTrue(!transportErrored); // Normal shutdown does not trigger the event. - assertTrue(exceptions.isEmpty()); - } - - @Test - public void testDataSentWithWriteAndFlushIsReceived() throws Exception { - try (NettyEchoServer server = createEchoServer()) { - server.start(); - - int port = server.getServerPort(); - - Transport transport = createTransport(createTransportOptions(), createSSLOptions()); - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - LOG.info("Connected to server:{}:{} as expected.", HOSTNAME, port); - } catch (Exception e) { - fail("Should not have failed to connect to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - - assertTrue(transport.isConnected()); - - ProtonBuffer sendBuffer = transport.getBufferAllocator().outputBuffer(SEND_BYTE_COUNT); - for (int i = 0; i < SEND_BYTE_COUNT; ++i) { - sendBuffer.writeByte((byte) 'A'); - } - - transport.writeAndFlush(sendBuffer, () -> LOG.debug("Netty reports write complete")); - - assertTrue(Wait.waitFor(new Wait.Condition() { - @Override - public boolean isSatisfied() throws Exception { - return !data.isEmpty(); - } - }, 10000, 50)); - - assertEquals(SEND_BYTE_COUNT, data.get(0).getReadableBytes()); - - transport.close(); - } - - assertTrue(!transportErrored); // Normal shutdown does not trigger the event. - assertTrue(exceptions.isEmpty()); - } - - @Test - public void testMultipleDataPacketsSentAreReceived() throws Exception { - doMultipleDataPacketsSentAndReceive(SEND_BYTE_COUNT, 1); - } - - @Test - public void testMultipleDataPacketsSentAreReceivedRepeatedly() throws Exception { - doMultipleDataPacketsSentAndReceive(SEND_BYTE_COUNT, 10); - } - - public void doMultipleDataPacketsSentAndReceive(final int byteCount, final int iterations) throws Exception { - try (NettyEchoServer server = createEchoServer()) { - server.start(); - - int port = server.getServerPort(); - - Transport transport = createTransport(createTransportOptions(), createSSLOptions()); - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - LOG.info("Connected to server:{}:{} as expected.", HOSTNAME, port); - } catch (Exception e) { - fail("Should not have failed to connect to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - - assertTrue(transport.isConnected()); - - final ProtonBuffer sendBuffer = allocator.allocate(byteCount); - - for (int i = 0; i < byteCount; ++i) { - sendBuffer.writeByte((byte) 'A'); - } - - for (int i = 0; i < iterations; ++i) { - transport.writeAndFlush(sendBuffer.copy(true)); - } - - assertTrue(Wait.waitFor(new Wait.Condition() { - @Override - public boolean isSatisfied() throws Exception { - return bytesRead.get() == (byteCount * iterations); - } - }, 10000, 50)); - - transport.close(); - } - - assertTrue(!transportErrored); // Normal shutdown does not trigger the event. - assertTrue(exceptions.isEmpty()); - } - - @Test - public void testSendToClosedTransportFails() throws Exception { - Transport transport = null; - - try (NettyEchoServer server = createEchoServer()) { - server.start(); - - int port = server.getServerPort(); - - transport = createTransport(createTransportOptions(), createSSLOptions()); - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - LOG.info("Connected to server:{}:{} as expected.", HOSTNAME, port); - } catch (Exception e) { - fail("Should not have failed to connect to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - - assertTrue(transport.isConnected()); - - transport.close(); - - final ProtonBuffer sendBuffer = allocator.allocate(10); - - try { - transport.writeAndFlush(sendBuffer); - fail("Should throw on send of closed transport"); - } catch (IOException ex) { - } - } - } - - @Test - public void testConnectRunsInitializationMethod() throws Exception { - try (NettyEchoServer server = createEchoServer()) { - server.start(); - - int port = server.getServerPort(); - final CountDownLatch initialized = new CountDownLatch(1); - - Transport transport = createTransport(createTransportOptions(), createSSLOptions()); - try { - transport.connect(HOSTNAME, port, new NettyTransportListener() { - - @Override - public void transportInitialized(Transport transport) { - initialized.countDown(); - assertFalse(transport.isConnected()); - } - }); - LOG.info("Connected to server:{}:{} as expected.", HOSTNAME, port); - } catch (Exception e) { - fail("Should not have failed to connect to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - - initialized.await(); - transport.awaitConnect(); - - assertTrue(transport.isConnected()); - assertEquals(HOSTNAME, transport.getHost(), "Server host is incorrect"); - assertEquals(port, transport.getPort(), "Server port is incorrect"); - assertEquals(0, initialized.getCount()); - - transport.close(); - } - - assertTrue(!transportErrored); // Normal shutdown does not trigger the event. - assertTrue(exceptions.isEmpty()); - assertTrue(data.isEmpty()); - } - - @Test - public void testFailureInInitializationRoutineFailsConnect() throws Exception { - try (NettyEchoServer server = createEchoServer()) { - server.start(); - - int port = server.getServerPort(); - - Transport transport = createTransport(createTransportOptions(), createSSLOptions()); - try { - transport.connect(HOSTNAME, port, new NettyTransportListener() { - - @Override - public void transportInitialized(Transport transport) { - throw new RuntimeException(); - } - }).awaitConnect(); - fail("Should not have connected to the server at " + HOSTNAME + ":" + port); - } catch (Exception e) { - LOG.info("Failed to connect to: {}:{} as expected.", HOSTNAME, port); - } - - assertFalse(transport.isConnected(), "Should not be connected"); - assertEquals(HOSTNAME, transport.getHost(), "Server host is incorrect"); - assertEquals(port, transport.getPort(), "Server port is incorrect"); - - transport.close(); - } - - assertTrue(transportErrored); - assertFalse(exceptions.isEmpty()); - assertTrue(data.isEmpty()); - } - - @Disabled("Used for checking for transport level leaks, my be unstable on CI.") - @Test - public void testSendToClosedTransportFailsButDoesNotLeak() throws Exception { - Transport transport = null; - - ResourceLeakDetector.setLevel(Level.PARANOID); - - try (NettyEchoServer server = createEchoServer()) { - server.start(); - - int port = server.getServerPort(); - - for (int i = 0; i < 256; ++i) { - transport = createTransport(createTransportOptions(), createSSLOptions()); - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - LOG.info("Connected to server:{}:{} as expected.", HOSTNAME, port); - } catch (Exception e) { - fail("Should not have failed to connect to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - - assertTrue(transport.isConnected()); - - ProtonBuffer sendBuffer = transport.getBufferAllocator().outputBuffer(10 * 1024 * 1024); - sendBuffer.writeBytes(new byte[] {0, 1, 2, 3, 4}); - - transport.close(); - - try { - transport.writeAndFlush(sendBuffer); - fail("Should throw on send of closed transport"); - } catch (IOException ex) { - } - } - - System.gc(); - } - } - - @Test - public void testCreateFailsIfUnknownPreferredNativeIOLayerSelected() throws Exception { - TransportOptions options = createTransportOptions(); - options.allowNativeIO(true); - options.nativeIOPreference("NATIVE-IO"); - - assertThrows(IllegalArgumentException.class, () -> createTransport(options, createSSLOptions())); - } - - @Test - public void testConnectToServerWithEpollEnabled() throws Exception { - doTestEpollSupport(true); - } - - @Test - public void testConnectToServerWithEpollDisabled() throws Exception { - doTestEpollSupport(false); - } - - private void doTestEpollSupport(boolean useEpoll) throws Exception { - assumeTrue(Epoll.isAvailable()); - - try (NettyEchoServer server = createEchoServer()) { - server.start(); - - int port = server.getServerPort(); - - TransportOptions options = createTransportOptions(); - options.allowNativeIO(useEpoll); - options.nativeIOPreference("EPOLL"); - Transport transport = createTransport(options, createSSLOptions()); - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - LOG.info("Connected to server:{}:{} as expected.", HOSTNAME, port); - } catch (Exception e) { - fail("Should not have failed to connect to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - - assertTrue(transport.isConnected()); - assertEquals(HOSTNAME, transport.getHost(), "Server host is incorrect"); - assertEquals(port, transport.getPort(), "Server port is incorrect"); - assertEpoll("Transport should be using Epoll", useEpoll, transport); - - transport.close(); - - // Additional close should not fail or cause other problems. - transport.close(); - } - - assertFalse(transportErrored); - assertTrue(exceptions.isEmpty()); - assertTrue(data.isEmpty()); - } - - @Disabled("IU_Uring support not ready for Netty 5") - @Test - public void testConnectToServerWithIOUringEnabled() throws Exception { - doTestIORingSupport(true); - } - - @Disabled("IU_Uring support not ready for Netty 5") - @Test - public void testConnectToServerWithIOUringDisabled() throws Exception { - doTestIORingSupport(false); - } - - private void doTestIORingSupport(boolean useIOUring) throws Exception { - assumeTrue(IOUring.isAvailable()); - - try (NettyEchoServer server = createEchoServer()) { - server.start(); - - int port = server.getServerPort(); - - TransportOptions options = createTransportOptions(); - options.allowNativeIO(useIOUring); - options.nativeIOPreference("IO_URING"); - Transport transport = createTransport(options, createSSLOptions()); - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - LOG.info("Connected to server:{}:{} as expected.", HOSTNAME, port); - } catch (Exception e) { - fail("Should not have failed to connect to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - - assertTrue(transport.isConnected()); - assertEquals(HOSTNAME, transport.getHost(), "Server host is incorrect"); - assertEquals(port, transport.getPort(), "Server port is incorrect"); - assertIOUring("Transport should be using URing", useIOUring, transport); - - transport.close(); - - // Additional close should not fail or cause other problems. - transport.close(); - } - - assertFalse(transportErrored); - assertTrue(exceptions.isEmpty()); - assertTrue(data.isEmpty()); - } - - @Test - public void testConnectToServerWithKQueueEnabled() throws Exception { - doTestKQueueSupport(true); - } - - @Test - public void testConnectToServerWithKQueueDisabled() throws Exception { - doTestKQueueSupport(false); - } - - private void doTestKQueueSupport(boolean useKQueue) throws Exception { - assumeTrue(KQueue.isAvailable()); - - try (NettyEchoServer server = createEchoServer()) { - server.start(); - - int port = server.getServerPort(); - - TransportOptions options = createTransportOptions(); - options.allowNativeIO(true); - Transport transport = createTransport(options, createSSLOptions()); - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - LOG.info("Connected to server:{}:{} as expected.", HOSTNAME, port); - } catch (Exception e) { - fail("Should not have failed to connect to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - - assertTrue(transport.isConnected()); - assertEquals(HOSTNAME, transport.getHost(), "Server host is incorrect"); - assertEquals(port, transport.getPort(), "Server port is incorrect"); - assertKQueue("Transport should be using Kqueue", useKQueue, transport); - - transport.close(); - - // Additional close should not fail or cause other problems. - transport.close(); - } - - assertTrue(!transportErrored); // Normal shutdown does not trigger the event. - assertTrue(exceptions.isEmpty()); - assertTrue(data.isEmpty()); - } - - @Test - public void testFallbackToNioWhenNativeIOConfiguredNotSupportedEpoll() throws Exception { - assumeFalse(Epoll.isAvailable()); - - doTestFallbackToNioWhenNativeLayerNotSupported(EpollSupport.NAME); - } - - @Test - public void testFallbackToNioWhenNativeIOConfiguredNotSupportedKQueue() throws Exception { - assumeFalse(KQueue.isAvailable()); - - doTestFallbackToNioWhenNativeLayerNotSupported(KQueueSupport.NAME); - } - - private void doTestFallbackToNioWhenNativeLayerNotSupported(String nativeIOLayer) throws Exception { - try (NettyEchoServer server = createEchoServer()) { - server.start(); - - int port = server.getServerPort(); - - TransportOptions options = createTransportOptions(); - options.allowNativeIO(true); - options.nativeIOPreference(nativeIOLayer); - - Transport transport = createTransport(options, createSSLOptions()); - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - LOG.info("Connected to server:{}:{} as expected.", HOSTNAME, port); - } catch (Exception e) { - fail("Should not have failed to connect to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - - assertTrue(transport.isConnected()); - assertEquals(HOSTNAME, transport.getHost(), "Server host is incorrect"); - assertEquals(port, transport.getPort(), "Server port is incorrect"); - - transport.close(); - - // Additional close should not fail or cause other problems. - transport.close(); - } - - assertFalse(transportErrored); - assertTrue(exceptions.isEmpty()); - assertTrue(data.isEmpty()); - } - - protected Netty5IOContext createContext(TransportOptions options, SslOptions sslOptions) { - if (context != null) { - throw new IllegalStateException("Test already has a defined Netty IO Context"); - } - - return this.context = new Netty5IOContext(options, sslOptions, getTestName()); - } - - protected Transport createTransport(TransportOptions options, SslOptions sslOptions) { - if (context != null) { - throw new IllegalStateException("Test already has a defined Netty IO Context"); - } else { - this.context = new Netty5IOContext(options, sslOptions, getTestName()); - } - - return context.newTransport(); - } - - protected TransportOptions createTransportOptions() { - return new TransportOptions(); - } - - protected SslOptions createSSLOptions() { - return new SslOptions().sslEnabled(false); - } - - protected TransportOptions createServerTransportOptions() { - return new TransportOptions(); - } - - protected SslOptions createServerSSLOptions() { - return new SslOptions().sslEnabled(false); - } - - protected void logTransportErrors() { - if (!exceptions.isEmpty()) { - for(Throwable ex : exceptions) { - LOG.info("Transport sent exception: {}", ex, ex); - } - } - } - - protected final NettyEchoServer createEchoServer() { - return createEchoServer(false); - } - - protected final NettyEchoServer createEchoServer(SslOptions options) { - return createEchoServer(options, false); - } - - protected final NettyEchoServer createEchoServer(boolean needClientAuth) { - return createEchoServer(createServerSSLOptions(), needClientAuth); - } - - protected final NettyEchoServer createEchoServer(SslOptions options, boolean needClientAuth) { - return createEchoServer(createServerTransportOptions(), options, needClientAuth); - } - - protected final NettyEchoServer createEchoServer(TransportOptions options) { - return new NettyEchoServer(options, createServerSSLOptions(), false); - } - - protected final NettyEchoServer createEchoServer(TransportOptions options, SslOptions sslOptions, boolean needClientAuth) { - return new NettyEchoServer(options, sslOptions, needClientAuth); - } - - public class NettyTransportListener implements TransportListener { - - @Override - public void transportRead(ProtonBuffer incoming) { - LOG.debug("Client has new incoming data of size: {}", incoming.getReadableBytes()); - bytesRead.addAndGet(incoming.getReadableBytes()); - data.add(incoming.transfer()); - } - - @Override - public void transportError(Throwable cause) { - LOG.info("Transport error caught: {}", cause.getMessage(), cause); - transportErrored = true; - exceptions.add(cause); - } - - @Override - public void transportConnected(Transport transport) { - transportConnected = true; - } - - @Override - public void transportInitialized(Transport transport) { - transportInitialized = true; - } - } - - private void assertEpoll(String message, boolean expected, Transport transport) throws Exception { - assertEventLoopIoHandlerType(message, expected, transport ,EpollHandler.class); - } - - private void assertKQueue(String message, boolean expected, Transport transport) throws Exception { - assertEventLoopIoHandlerType(message, expected, transport, KQueueHandler.class); - } - - private void assertIOUring(String message, boolean expected, Transport transport) throws Exception { - //assertEventLoopIoHandlerType(message, expected, transport, IOUringHandler.class); - fail("Netty 5 doesn't have IO_Uring support yet"); - } - - private static void assertEventLoopIoHandlerType(String message, boolean expected, Transport transport, Class ioHandlerClass) throws Exception { - Field bootstrap = null; - Class transportType = transport.getClass(); - - while (transportType != null && bootstrap == null) { - try { - bootstrap = transportType.getDeclaredField("bootstrap"); - } catch (NoSuchFieldException error) { - transportType = transportType.getSuperclass(); - if (Object.class.equals(transportType)) { - transportType = null; - } - } - } - - assertNotNull(bootstrap, "Transport implementation unknown"); - - bootstrap.setAccessible(true); - - final Bootstrap transportBootstrap = (Bootstrap) bootstrap.get(transport); - - assertTrue(MultithreadEventLoopGroup.class.isInstance(transportBootstrap.config().group()), message); - assertTrue(SingleThreadEventLoop.class.isInstance(transportBootstrap.config().group().next()), message); - - Field ioHandlerField = null; - Class executorType = transportBootstrap.config().group().next().getClass(); - - while (executorType != null && ioHandlerField == null) { - try { - ioHandlerField = executorType.getDeclaredField("ioHandler"); - } catch (NoSuchFieldException error) { - executorType = executorType.getSuperclass(); - if (Object.class.equals(executorType)) { - executorType = null; - } - } - } - - assertNotNull(ioHandlerField, "EventLoop implementation unknown"); - - ioHandlerField.setAccessible(true); - - final IoHandler ioHandlerRef = (IoHandler) ioHandlerField.get(transportBootstrap.config().group().next()); - - if (expected) { - assertTrue(ioHandlerClass.isInstance(ioHandlerRef), message); - } else { - assertFalse(ioHandlerClass.isInstance(ioHandlerRef), message); - } - } -} diff --git a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/WebSocketTransportTest.java b/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/WebSocketTransportTest.java deleted file mode 100644 index 8b850b28..00000000 --- a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/WebSocketTransportTest.java +++ /dev/null @@ -1,504 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.qpid.protonj2.client.transport.netty5; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.apache.qpid.protonj2.buffer.ProtonBuffer; -import org.apache.qpid.protonj2.buffer.ProtonBufferAllocator; -import org.apache.qpid.protonj2.client.SslOptions; -import org.apache.qpid.protonj2.client.TransportOptions; -import org.apache.qpid.protonj2.client.test.Wait; -import org.apache.qpid.protonj2.client.transport.Transport; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.netty5.bootstrap.Bootstrap; -import io.netty5.handler.codec.http.headers.HttpHeaders; -import io.netty5.handler.codec.http.websocketx.WebSocketServerHandshakeCompletionEvent; - -/** - * Test the Netty based WebSocket Transport - */ -@Timeout(30) -public class WebSocketTransportTest extends TcpTransportTest { - - private static final Logger LOG = LoggerFactory.getLogger(WebSocketTransportTest.class); - - @Override - protected TransportOptions createTransportOptions() { - return new TransportOptions().useWebSockets(true); - } - - @Override - protected TransportOptions createServerTransportOptions() { - return new TransportOptions().useWebSockets(true); - } - - @Override - @Test - public void testCannotCreateWithIllegalArgs() throws Exception { - assertThrows(IllegalArgumentException.class, () -> new WebSocketTransport(null, createTransportOptions(), createSSLOptions())); - assertThrows(IllegalArgumentException.class, () -> new WebSocketTransport(new Bootstrap(), null, createSSLOptions())); - assertThrows(IllegalArgumentException.class, () -> new WebSocketTransport(new Bootstrap(), createTransportOptions(), null)); - } - - @Test - public void testConnectToServerUsingCorrectPath() throws Exception { - final String WEBSOCKET_PATH = "/testpath"; - - try (NettyEchoServer server = createEchoServer()) { - server.setWebSocketPath(WEBSOCKET_PATH); - server.start(); - - final int port = server.getServerPort(); - - Transport transport = createTransport(createTransportOptions().webSocketPath(WEBSOCKET_PATH), createSSLOptions()); - - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - LOG.info("Connected to server:{}:{} as expected.", HOSTNAME, port); - } catch (Exception e) { - fail("Should have connected to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - - assertTrue(transport instanceof WebSocketTransport); - assertTrue(transport.isConnected()); - assertEquals(HOSTNAME, transport.getHost(), "Server host is incorrect"); - assertEquals(port, transport.getPort(), "Server port is incorrect"); - - transport.close(); - - // Additional close should not fail or cause other problems. - transport.close(); - } - - assertTrue(!transportErrored); // Normal shutdown does not trigger the event. - assertTrue(exceptions.isEmpty()); - assertTrue(data.isEmpty()); - } - - @Test - public void testConnectToServerUsingIncorrectPath() throws Exception { - final String WEBSOCKET_PATH = "/testpath"; - - try (NettyEchoServer server = createEchoServer()) { - // No configured path means it won't match the requested one. - server.start(); - - final int port = server.getServerPort(); - - server.close(); - - Transport transport = createTransport(createTransportOptions().webSocketPath(WEBSOCKET_PATH), createSSLOptions()); - - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - fail("Should have failed to connect to the server: " + HOSTNAME + ":" + port); - } catch (Exception e) { - LOG.info("Failed to connect to: {}:{} as expected.", HOSTNAME, port); - } - - assertTrue(transport instanceof WebSocketTransport); - assertFalse(transport.isConnected()); - - transport.close(); - } - - assertTrue(transportErrored); - assertFalse(exceptions.isEmpty()); - assertTrue(data.isEmpty()); - } - - @Test - public void testConnectionsSendReceiveLargeDataWhenFrameSizeAllowsIt() throws Exception { - final int FRAME_SIZE = 8192; - - final ProtonBuffer sendBuffer = allocator.allocate(FRAME_SIZE); - for (int i = 0; i < FRAME_SIZE; ++i) { - sendBuffer.writeByte((byte) 'A'); - } - - try (NettyEchoServer server = createEchoServer()) { - // Server should pass the data through without issue with this size - server.setMaxFrameSize(FRAME_SIZE); - server.start(); - - final int port = server.getServerPort(); - - List transports = new ArrayList<>(); - - Transport transport = createTransport(createTransportOptions().webSocketMaxFrameSize(FRAME_SIZE), createSSLOptions()); - - try { - // The transport should allow for the size of data we sent. - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - transports.add(transport); - transport.writeAndFlush(sendBuffer.copy()); - } catch (Exception e) { - fail("Should have connected to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - - assertTrue(transport instanceof WebSocketTransport); - assertTrue(Wait.waitFor(new Wait.Condition() { - @Override - public boolean isSatisfied() throws Exception { - LOG.debug("Checking completion: read {} expecting {}", bytesRead.get(), FRAME_SIZE); - return bytesRead.get() == FRAME_SIZE || !transport.isConnected(); - } - }, 10000, 50)); - - assertTrue(transport.isConnected(), "Connection failed while receiving."); - - transport.close(); - } - - assertTrue(exceptions.isEmpty()); - } - - @Test - public void testConnectionReceivesFragmentedDataSingleWriteAndFlush() throws Exception { - testConnectionReceivesFragmentedData(true); - } - - @Test - public void testConnectionReceivesFragmentedDataWriteThenFlush() throws Exception { - testConnectionReceivesFragmentedData(false); - } - - private void testConnectionReceivesFragmentedData(boolean writeAndFlush) throws Exception { - - final int FRAME_SIZE = 5317; - - final ProtonBuffer sendBuffer = allocator.allocate(FRAME_SIZE); - for (int i = 0; i < FRAME_SIZE; ++i) { - sendBuffer.writeByte((byte) ('A' + (i % 10))); - } - - try (NettyEchoServer server = createEchoServer()) { - server.setMaxFrameSize(FRAME_SIZE); - // Server should fragment the data as it goes through - server.setFragmentWrites(true); - server.start(); - - final int port = server.getServerPort(); - - List transports = new ArrayList<>(); - - TransportOptions clientOptions = createTransportOptions(); - clientOptions.traceBytes(true); - clientOptions.webSocketMaxFrameSize(FRAME_SIZE); - - NettyTransportListener wsListener = new NettyTransportListener(); - - Transport transport = createTransport(clientOptions, createSSLOptions()); - try { - transport.connect(HOSTNAME, port, wsListener).awaitConnect(); - transports.add(transport); - if (writeAndFlush) { - transport.writeAndFlush(allocator.allocate()); - transport.writeAndFlush(sendBuffer.copy()); - } else { - transport.write(allocator.allocate()); - transport.write(sendBuffer.copy()); - transport.flush(); - } - } catch (Exception e) { - fail("Should have connected to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - - assertTrue(transport instanceof WebSocketTransport); - assertTrue(Wait.waitFor(new Wait.Condition() { - @Override - public boolean isSatisfied() throws Exception { - LOG.debug("Checking completion: read {} expecting {}", bytesRead.get(), FRAME_SIZE); - return bytesRead.get() == FRAME_SIZE || !transport.isConnected(); - } - }, 10000, 50)); - - assertTrue(transport.isConnected(), "Connection failed while receiving."); - - transport.close(); - - assertEquals(2, data.size(), "Expected 2 data packets due to separate websocket frames"); - - ProtonBuffer receivedBuffer = ProtonBufferAllocator.defaultAllocator().allocate(FRAME_SIZE); - for (ProtonBuffer buf : data) { - buf.copyInto(buf.getReadOffset(), receivedBuffer, receivedBuffer.getWriteOffset(), buf.getReadableBytes()); - receivedBuffer.advanceWriteOffset(buf.getReadableBytes()); - } - - assertEquals(FRAME_SIZE, receivedBuffer.getReadableBytes(), "Unexpected data length"); - assertEquals(sendBuffer, receivedBuffer, "Unexpected data"); - } finally { - for (ProtonBuffer buf : data) { - buf.close(); - } - } - - assertTrue(exceptions.isEmpty()); - } - - @Test - public void testConnectionsSendReceiveLargeDataFailsDueToMaxFrameSize() throws Exception { - final int FRAME_SIZE = 1024; - - final ProtonBuffer sendBuffer = allocator.allocate(FRAME_SIZE); - for (int i = 0; i < FRAME_SIZE; ++i) { - sendBuffer.writeByte((byte) 'A'); - } - - try (NettyEchoServer server = createEchoServer()) { - // Server should pass the data through, client should choke on the incoming size. - server.setMaxFrameSize(FRAME_SIZE); - server.start(); - - final int port = server.getServerPort(); - - List transports = new ArrayList<>(); - - final Transport transport = createTransport(createTransportOptions().webSocketMaxFrameSize(FRAME_SIZE / 2), createSSLOptions()); - - try { - // Transport can't receive anything bigger so it should fail the connection - // when data arrives that is larger than this value. - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - transports.add(transport); - transport.writeAndFlush(sendBuffer.copy()); - } catch (Exception e) { - fail("Should have connected to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - - assertTrue(transport instanceof WebSocketTransport); - assertTrue(Wait.waitFor(() -> !transport.isConnected()), "Transport should have lost connection"); - } - - assertFalse(exceptions.isEmpty()); - } - - @Test - public void testTransportDetectsConnectionDropWhenServerEnforcesMaxFrameSize() throws Exception { - final int FRAME_SIZE = 1024; - - final ProtonBuffer sendBuffer = allocator.allocate(FRAME_SIZE); - for (int i = 0; i < FRAME_SIZE; ++i) { - sendBuffer.writeByte((byte) 'A'); - } - - try (NettyEchoServer server = createEchoServer()) { - // Server won't accept the data as it's to large and will close the connection. - server.setMaxFrameSize(FRAME_SIZE / 2); - server.start(); - - final int port = server.getServerPort(); - - List transports = new ArrayList<>(); - - final Transport transport = createTransport(createTransportOptions().webSocketMaxFrameSize(FRAME_SIZE), createSSLOptions()); - - assertTrue(transport instanceof WebSocketTransport); - - try { - // Transport allows bigger frames in so that server is the one causing the failure. - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - transports.add(transport); - transport.writeAndFlush(sendBuffer.copy()); - } catch (Exception e) { - fail("Should have connected to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - - assertTrue(Wait.waitFor(new Wait.Condition() { - @Override - public boolean isSatisfied() throws Exception { - try { - transport.writeAndFlush(sendBuffer.copy()); - } catch (IOException e) { - LOG.info("Transport send caught error:", e); - return true; - } - - return false; - } - }, 10000, 10), "Transport should have lost connection"); - - transport.close(); - } - } - - @Test - public void testTransportConnectionDoesNotDropWhenServerAndClientUseCompressionWithLargePayloads() throws Exception { - final int FRAME_SIZE = 16384; // This value would exceed the set max frame size without compression. - - final ProtonBuffer sendBuffer = allocator.allocate(FRAME_SIZE); - for (int i = 0; i < FRAME_SIZE; ++i) { - sendBuffer.writeByte((byte) 'A'); - } - - try (NettyEchoServer server = createEchoServer(createServerTransportOptions().webSocketCompression(true).traceBytes(true))) { - // Server won't accept the data as it's to large and will close the connection. - server.setMaxFrameSize(FRAME_SIZE / 2); - server.start(); - - final int port = server.getServerPort(); - - List transports = new ArrayList<>(); - - final Transport transport = createTransport(createTransportOptions().webSocketMaxFrameSize(FRAME_SIZE) - .webSocketCompression(true), createSSLOptions()); - - assertTrue(transport instanceof WebSocketTransport); - - try { - // Transport allows bigger frames in so that server is the one causing the failure. - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - transports.add(transport); - transport.writeAndFlush(sendBuffer.copy()); - } catch (Exception e) { - fail("Should have connected to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - - assertTrue(Wait.waitFor(new Wait.Condition() { - @Override - public boolean isSatisfied() throws Exception { - try { - transport.writeAndFlush(sendBuffer.copy()); - } catch (IOException e) { - LOG.info("Transport send caught error:", e); - return false; - } - - return true; - } - }, 10000, 10), "Transport should not have lost connection"); - - transport.close(); - } - } - - @Test - public void testConfiguredHttpHeadersArriveAtServer() throws Exception { - try (NettyEchoServer server = createEchoServer()) { - server.start(); - - final int port = server.getServerPort(); - - TransportOptions clientOptions = createTransportOptions(); - clientOptions.addWebSocketHeader("test-header1", "FOO"); - clientOptions.webSocketHeaders().put("test-header2", "BAR"); - - final Transport transport = createTransport(clientOptions, createSSLOptions()); - - assertTrue(transport instanceof WebSocketTransport); - - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - LOG.info("Connected to server:{}:{} as expected.", HOSTNAME, port); - } catch (Exception e) { - fail("Should have connected to the server at " + HOSTNAME + ":" + port + " but got exception: " + e); - } - - assertTrue(transport.isConnected()); - assertEquals(HOSTNAME, transport.getHost(), "Server host is incorrect"); - assertEquals(port, transport.getPort(), "Server port is incorrect"); - - assertTrue(server.awaitHandshakeCompletion(2000), "HandshakeCompletion not set within given time"); - - WebSocketServerHandshakeCompletionEvent handshake = server.getHandshakeComplete(); - assertNotNull(handshake, "completion should not be null"); - HttpHeaders requestHeaders = handshake.requestHeaders(); - - assertTrue(requestHeaders.contains("test-header1")); - assertTrue(requestHeaders.contains("test-header2")); - - assertEquals("FOO", requestHeaders.get("test-header1")); - assertEquals("BAR", requestHeaders.get("test-header2")); - - transport.close(); - } - - assertTrue(!transportErrored); // Normal shutdown does not trigger the event. - assertTrue(exceptions.isEmpty()); - assertTrue(data.isEmpty()); - } - - private static final String BROKER_JKS_KEYSTORE = "src/test/resources/broker-jks.keystore"; - private static final String PASSWORD = "password"; - - @Test - public void testNonSslWebSocketConnectionFailsToSslServer() throws Exception { - SslOptions serverSslOptions = new SslOptions(); - serverSslOptions.keyStoreLocation(BROKER_JKS_KEYSTORE); - serverSslOptions.keyStorePassword(PASSWORD); - serverSslOptions.verifyHost(false); - serverSslOptions.sslEnabled(true); - - try (NettyBlackHoleServer server = new NettyBlackHoleServer(createServerTransportOptions(), serverSslOptions)) { - server.start(); - - final int port = server.getServerPort(); - - TransportOptions clientOptions = createTransportOptions(); - - final Transport transport = createTransport(clientOptions, createSSLOptions()); - - assertTrue(transport instanceof WebSocketTransport); - - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - fail("should not have connected"); - } catch (Exception e) { - LOG.trace("Failed to connect with message: {}", e.getMessage()); - } - } - } - - @Test - public void testWebsocketConnectionToBlackHoleServerTimesOut() throws Exception { - try (NettyBlackHoleServer server = new NettyBlackHoleServer(new TransportOptions(), new SslOptions().sslEnabled(false))) { - server.start(); - - final int port = server.getServerPort(); - - TransportOptions clientOptions = createTransportOptions(); - clientOptions.connectTimeout(25); - - final Transport transport = createTransport(clientOptions, createSSLOptions()); - - assertTrue(transport instanceof WebSocketTransport); - - try { - transport.connect(HOSTNAME, port, testListener).awaitConnect(); - fail("should not have connected"); - } catch (Exception e) { - String message = e.getMessage(); - assertNotNull(message); - assertTrue(message.contains("WebSocket handshake timed out"), "Unexpected message: " + message); - } - } - } -} diff --git a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/X509AliasKeyManagerTest.java b/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/X509AliasKeyManagerTest.java deleted file mode 100644 index 3373422d..00000000 --- a/protonj2-client/src/test/java/org/apache/qpid/protonj2/client/transport/netty5/X509AliasKeyManagerTest.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.qpid.protonj2.client.transport.netty5; - -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.net.Socket; -import java.security.Principal; -import java.security.PrivateKey; -import java.security.cert.X509Certificate; - -import javax.net.ssl.SSLEngine; -import javax.net.ssl.X509ExtendedKeyManager; - -import org.apache.qpid.protonj2.client.transport.X509AliasKeyManager; -import org.junit.jupiter.api.Test; - -public class X509AliasKeyManagerTest { - - @Test - public void testNullAliasCausesIAE() { - try { - new X509AliasKeyManager(null, mock(X509ExtendedKeyManager.class)); - fail("Expected an exception to be thrown"); - } catch (IllegalArgumentException iae) { - // expected - } - } - - @Test - public void testChooseClientAliasReturnsGivenAlias() { - String wrapperAlias = "wrapperAlias"; - String myDelegateAlias = "delegateAlias"; - X509ExtendedKeyManager mock = mock(X509ExtendedKeyManager.class); - when(mock.chooseClientAlias(any(String[].class), any(Principal[].class), any(Socket.class))).thenReturn(myDelegateAlias); - - X509ExtendedKeyManager wrapper = new X509AliasKeyManager(wrapperAlias, mock); - - assertEquals(wrapperAlias, wrapper.chooseClientAlias(new String[0], new Principal[0], new Socket()), "Expected wrapper alias"); - } - - @Test - public void testChooseServerAliasReturnsGivenAlias() { - String wrapperAlias = "wrapperAlias"; - String myDelegateAlias = "delegateAlias"; - X509ExtendedKeyManager mock = mock(X509ExtendedKeyManager.class); - when(mock.chooseServerAlias(any(String.class), any(Principal[].class), any(Socket.class))).thenReturn(myDelegateAlias); - - X509ExtendedKeyManager wrapper = new X509AliasKeyManager(wrapperAlias, mock); - - assertEquals(wrapperAlias, wrapper.chooseServerAlias("", new Principal[0], new Socket()), "Expected wrapper alias"); - } - - @Test - public void testGetCertificateChainDelegates() { - String wrapperAlias = "wrapperAlias"; - X509Certificate[] certs = new X509Certificate[7]; - - X509ExtendedKeyManager mock = mock(X509ExtendedKeyManager.class); - when(mock.getCertificateChain(any(String.class))).thenReturn(certs); - - X509ExtendedKeyManager wrapper = new X509AliasKeyManager(wrapperAlias, mock); - - assertSame(certs, wrapper.getCertificateChain(wrapperAlias), "Different object returned"); - } - - @Test - public void testGetClientAliasesReturnsGivenAliasOnly() { - String wrapperAlias = "wrapperAlias"; - String[] delegateAliases = new String[] { "a", "b", wrapperAlias}; - - X509ExtendedKeyManager mock = mock(X509ExtendedKeyManager.class); - when(mock.getClientAliases(any(String.class), any(Principal[].class))).thenReturn(delegateAliases); - - X509ExtendedKeyManager wrapper = new X509AliasKeyManager(wrapperAlias, mock); - - assertArrayEquals(new String[] { wrapperAlias }, wrapper.getClientAliases("", new Principal[0]), "Expected array containing only the wrapper alias"); - } - - @Test - public void testGetServerAliasesReturnsGivenAliasOnly() { - String wrapperAlias = "wrapperAlias"; - String[] delegateAliases = new String[] { "a", "b", wrapperAlias}; - - X509ExtendedKeyManager mock = mock(X509ExtendedKeyManager.class); - when(mock.getServerAliases(any(String.class), any(Principal[].class))).thenReturn(delegateAliases); - - X509ExtendedKeyManager wrapper = new X509AliasKeyManager(wrapperAlias, mock); - - assertArrayEquals(new String[] { wrapperAlias }, wrapper.getServerAliases("", new Principal[0]), "Expected array containing only the wrapper alias"); - } - - @Test - public void testGetPrivateKeyDelegates() { - String wrapperAlias = "wrapperAlias"; - PrivateKey mockKey = mock(PrivateKey.class); - - X509ExtendedKeyManager mock = mock(X509ExtendedKeyManager.class); - when(mock.getPrivateKey(any(String.class))).thenReturn(mockKey); - - X509ExtendedKeyManager wrapper = new X509AliasKeyManager(wrapperAlias, mock); - - assertSame(mockKey, wrapper.getPrivateKey(wrapperAlias), "Different object returned"); - } - - @Test - public void testChooseEngineClientAliasReturnsGivenAlias() { - String wrapperAlias = "wrapperAlias"; - String myDelegateAlias = "delegateAlias"; - X509ExtendedKeyManager mock = mock(X509ExtendedKeyManager.class); - when(mock.chooseEngineClientAlias(any(String[].class), any(Principal[].class), any(SSLEngine.class))).thenReturn(myDelegateAlias); - - X509ExtendedKeyManager wrapper = new X509AliasKeyManager(wrapperAlias, mock); - - assertEquals(wrapperAlias, wrapper.chooseEngineClientAlias(new String[0], new Principal[0], mock(SSLEngine.class)), "Expected wrapper alias"); - } - - @Test - public void testChooseEngineServerAliasReturnsGivenAlias() { - String wrapperAlias = "wrapperAlias"; - String myDelegateAlias = "delegateAlias"; - X509ExtendedKeyManager mock = mock(X509ExtendedKeyManager.class); - when(mock.chooseEngineServerAlias(any(String.class), any(Principal[].class), any(SSLEngine.class))).thenReturn(myDelegateAlias); - - X509ExtendedKeyManager wrapper = new X509AliasKeyManager(wrapperAlias, mock); - - assertEquals(wrapperAlias, wrapper.chooseEngineServerAlias("", new Principal[0], mock(SSLEngine.class)), "Expected wrapper alias"); - } -} diff --git a/protonj2-performance-tests/pom.xml b/protonj2-performance-tests/pom.xml index 161e0da4..dfb090df 100644 --- a/protonj2-performance-tests/pom.xml +++ b/protonj2-performance-tests/pom.xml @@ -20,7 +20,7 @@ org.apache.qpid protonj2-parent - 1.0.0-M22-SNAPSHOT + 1.0.0-M23-SNAPSHOT protonj2-performance-tests diff --git a/protonj2-test-driver/pom.xml b/protonj2-test-driver/pom.xml index 51fb8487..670de206 100644 --- a/protonj2-test-driver/pom.xml +++ b/protonj2-test-driver/pom.xml @@ -20,7 +20,7 @@ org.apache.qpid protonj2-parent - 1.0.0-M22-SNAPSHOT + 1.0.0-M23-SNAPSHOT protonj2-test-driver @@ -54,32 +54,6 @@ netty-codec-http ${netty-scope} - - - io.netty - netty5-buffer - ${netty5-scope} - - - io.netty - netty5-common - ${netty5-scope} - - - io.netty - netty5-handler - ${netty5-scope} - - - io.netty - netty5-transport - ${netty5-scope} - - - io.netty - netty5-codec-http - ${netty5-scope} - org.slf4j diff --git a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/netty/NettyIOBuilder.java b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/netty/NettyIOBuilder.java index 39d77db9..29e306d0 100644 --- a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/netty/NettyIOBuilder.java +++ b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/netty/NettyIOBuilder.java @@ -25,9 +25,6 @@ import org.apache.qpid.protonj2.test.driver.netty.netty4.Netty4Client; import org.apache.qpid.protonj2.test.driver.netty.netty4.Netty4Server; import org.apache.qpid.protonj2.test.driver.netty.netty4.Netty4Support; -import org.apache.qpid.protonj2.test.driver.netty.netty5.Netty5Client; -import org.apache.qpid.protonj2.test.driver.netty.netty5.Netty5Server; -import org.apache.qpid.protonj2.test.driver.netty.netty5.Netty5Support; /** * An I/O context used to abstract the implementation of the IO layer in use. @@ -49,8 +46,6 @@ public interface NettyIOBuilder { public static NettyClient createClient(ProtonTestClientOptions options, Runnable connectedHandler, Consumer inputHandler) { if (Netty4Support.isAvailable()) { return new Netty4Client(options, connectedHandler, inputHandler); - } else if (Netty5Support.isAvailable()) { - return new Netty5Client(options, connectedHandler, inputHandler); } throw new UnsupportedOperationException("Netty not available on the class path"); @@ -73,8 +68,6 @@ public static NettyClient createClient(ProtonTestClientOptions options, Runnable public static NettyServer createServer(ProtonTestServerOptions options, Runnable connectedHandler, Runnable disconnectedHandler, Consumer inputHandler) { if (Netty4Support.isAvailable()) { return new Netty4Server(options, connectedHandler, disconnectedHandler, inputHandler); - } else if (Netty5Support.isAvailable()) { - return new Netty5Server(options, connectedHandler, disconnectedHandler, inputHandler); } throw new UnsupportedOperationException("Netty not available on the class path"); diff --git a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/netty/netty5/Netty5Client.java b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/netty/netty5/Netty5Client.java deleted file mode 100644 index b17209e2..00000000 --- a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/netty/netty5/Netty5Client.java +++ /dev/null @@ -1,564 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.qpid.protonj2.test.driver.netty.netty5; - -import java.io.IOException; -import java.lang.invoke.MethodHandles; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.Objects; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Consumer; - -import org.apache.qpid.protonj2.test.driver.ProtonTestClientOptions; -import org.apache.qpid.protonj2.test.driver.netty.NettyClient; -import org.apache.qpid.protonj2.test.driver.netty.NettyEventLoop; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.netty5.bootstrap.Bootstrap; -import io.netty5.buffer.Buffer; -import io.netty5.buffer.BufferAllocator; -import io.netty5.channel.Channel; -import io.netty5.channel.ChannelFutureListeners; -import io.netty5.channel.ChannelHandler; -import io.netty5.channel.ChannelHandlerAdapter; -import io.netty5.channel.ChannelHandlerContext; -import io.netty5.channel.ChannelInitializer; -import io.netty5.channel.ChannelOption; -import io.netty5.channel.EventLoop; -import io.netty5.channel.EventLoopGroup; -import io.netty5.channel.MultithreadEventLoopGroup; -import io.netty5.channel.SimpleChannelInboundHandler; -import io.netty5.channel.nio.NioHandler; -import io.netty5.channel.socket.nio.NioSocketChannel; -import io.netty5.handler.codec.http.DefaultHttpContent; -import io.netty5.handler.codec.http.FullHttpRequest; -import io.netty5.handler.codec.http.FullHttpResponse; -import io.netty5.handler.codec.http.HttpClientCodec; -import io.netty5.handler.codec.http.HttpObjectAggregator; -import io.netty5.handler.codec.http.headers.HttpHeaders; -import io.netty5.handler.codec.http.websocketx.BinaryWebSocketFrame; -import io.netty5.handler.codec.http.websocketx.CloseWebSocketFrame; -import io.netty5.handler.codec.http.websocketx.ContinuationWebSocketFrame; -import io.netty5.handler.codec.http.websocketx.PingWebSocketFrame; -import io.netty5.handler.codec.http.websocketx.PongWebSocketFrame; -import io.netty5.handler.codec.http.websocketx.TextWebSocketFrame; -import io.netty5.handler.codec.http.websocketx.WebSocketClientHandshaker; -import io.netty5.handler.codec.http.websocketx.WebSocketClientHandshakerFactory; -import io.netty5.handler.codec.http.websocketx.WebSocketFrame; -import io.netty5.handler.codec.http.websocketx.WebSocketVersion; -import io.netty5.handler.codec.http.websocketx.extensions.compression.WebSocketClientCompressionHandler; -import io.netty5.handler.logging.LoggingHandler; -import io.netty5.handler.ssl.SslHandler; -import io.netty5.util.concurrent.Future; -import io.netty5.util.concurrent.FutureListener; - -/** - * Self contained Netty client implementation that provides a base for more - * complex client implementations to use as the IO layer. - */ -public final class Netty5Client implements NettyClient { - - private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - - private static final String AMQP_SUB_PROTOCOL = "amqp"; - private static final int SHUTDOWN_TIMEOUT = 50; - - private Netty5EventLoop eventLoop; - private Bootstrap bootstrap; - private EventLoopGroup group; - private Channel channel; - private String host; - private int port; - private boolean wsCompressionRequest; - private boolean wsCompressionResponse; - protected volatile IOException failureCause; - private final ProtonTestClientOptions options; - private volatile SslHandler sslHandler; - protected final AtomicBoolean connected = new AtomicBoolean(); - protected final AtomicBoolean closed = new AtomicBoolean(); - protected final CountDownLatch connectedLatch = new CountDownLatch(1); - - private final Consumer inputConsumer; - private final Runnable connectedRunnable; - - public Netty5Client(ProtonTestClientOptions options, Runnable connectedRunnable, Consumer inputConsumer) { - Objects.requireNonNull(options); - Objects.requireNonNull(inputConsumer); - Objects.requireNonNull(connectedRunnable); - - this.options = options; - this.connectedRunnable = connectedRunnable; - this.inputConsumer = inputConsumer; - } - - @Override - public void close() throws Exception { - if (closed.compareAndSet(false, true)) { - connected.set(false); - connectedLatch.countDown(); - if (channel != null) { - try { - if (!channel.close().asStage().await(10, TimeUnit.SECONDS)) { - LOG.info("Channel close timed out waiting for result"); - } - } catch (InterruptedException e) { - Thread.interrupted(); - LOG.debug("Close of channel interrupted while awaiting result"); - } - } - - if (group != null && !group.isShutdown()) { - group.shutdownGracefully(0, SHUTDOWN_TIMEOUT, TimeUnit.MILLISECONDS); - try { - if (eventLoop != null && eventLoop.inEventLoop()) { - // If scripted close we might be inside the event loop and - // we cannot wait in that case. - return; - } - - if (!group.awaitTermination(2 * SHUTDOWN_TIMEOUT, TimeUnit.MILLISECONDS)) { - LOG.trace("Connection IO Event Loop shutdown failed to complete in allotted time"); - } - } catch (InterruptedException e) { - Thread.interrupted(); - LOG.debug("Shutdown of netty event loop interrupted while awaiting result"); - } - } - } - } - - @Override - public void connect(String host, int port) throws IOException { - if (closed.get()) { - throw new IllegalStateException("Netty client has already been closed"); - } - - if (host == null || host.isEmpty()) { - throw new IllegalArgumentException("Transport host value cannot be null"); - } - - this.host = host; - - if (port > 0) { - this.port = port; - } else { - if (options.isSecure()) { - this.port = ProtonTestClientOptions.DEFAULT_SSL_PORT; - } else { - this.port = ProtonTestClientOptions.DEFAULT_TCP_PORT; - } - } - - group = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - bootstrap = new Bootstrap().channel(NioSocketChannel.class).group(group); - bootstrap.handler(new ChannelInitializer() { - @Override - public void initChannel(Channel transportChannel) throws Exception { - channel = transportChannel; - eventLoop = new Netty5EventLoop(channel.executor()); - configureChannel(transportChannel); - } - }); - - configureNetty(bootstrap, options); - - bootstrap.connect(host, port).addListener(channel, ChannelFutureListeners.FIRE_EXCEPTION_ON_FAILURE); - try { - connectedLatch.await(); - } catch (InterruptedException e) { - Thread.interrupted(); - } - - if (!connected.get()) { - if (failureCause != null) { - throw failureCause; - } else { - throw new IOException("Netty client was closed before a connection was established."); - } - } - } - - @Override - public NettyEventLoop eventLoop() { - if (channel == null || !channel.isActive()) { - throw new IllegalStateException("Channel is not connected or has closed"); - } - - return eventLoop; - } - - @Override - public void write(ByteBuffer buffer) { - if (channel == null || !channel.isActive()) { - throw new IllegalStateException("Channel is not connected or has closed"); - } - - channel.writeAndFlush(BufferAllocator.onHeapUnpooled().copyOf(buffer).makeReadOnly()); - } - - @Override - public boolean isConnected() { - return connected.get(); - } - - @Override - public boolean isSecure() { - return options.isSecure(); - } - - @Override - public boolean isWSCompressionActive() { - if (channel == null || !channel.isActive()) { - throw new IllegalStateException("Channel is not connected or has closed"); - } - - return wsCompressionRequest && wsCompressionResponse; - } - - @Override - public URI getRemoteURI() { - if (host != null) { - try { - if (options.isUseWebSockets()) { - return new URI(options.isSecure() ? "wss" : "ws", null, host, port, options.getWebSocketPath(), null, null); - } else { - return new URI(options.isSecure() ? "ssl" : "tcp", null, host, port, null, null, null); - } - } catch (URISyntaxException e) { - } - } - - return null; - } - - //----- Default implementation of Netty handler - - protected class NettyClientInboundHandler implements ChannelHandler { - - private final WebSocketClientHandshaker handshaker; - private Future handshakeTimeoutFuture; - - public NettyClientInboundHandler() { - if (options.isUseWebSockets()) { - HttpHeaders headers = HttpHeaders.newHeaders(); - - options.getHttpHeaders().forEach((key, value) -> { - headers.set(key, value); - }); - - handshaker = WebSocketClientHandshakerFactory.newHandshaker( - getRemoteURI(), WebSocketVersion.V13, AMQP_SUB_PROTOCOL, - true, headers, options.getWebSocketMaxFrameSize()); - } else { - handshaker = null; - } - } - - @Override - public final void channelRegistered(ChannelHandlerContext context) throws Exception { - channel = context.channel(); - } - - @Override - public void channelActive(ChannelHandlerContext context) throws Exception { - if (options.isUseWebSockets()) { - handshaker.handshake(context.channel()); - - handshakeTimeoutFuture = context.executor().schedule(()-> { - LOG.trace("WebSocket handshake timed out! Channel is {}", context.channel()); - if (!handshaker.isHandshakeComplete()) { - Netty5Client.this.handleTransportFailure(channel, new IOException("WebSocket handshake timed out")); - } - }, options.getConnectTimeout(), TimeUnit.MILLISECONDS); - } - - // In the Secure case we need to let the handshake complete before we - // trigger the connected event. - if (!isSecure()) { - if (!options.isUseWebSockets()) { - handleConnected(context.channel()); - } - } else { - SslHandler sslHandler = context.pipeline().get(SslHandler.class); - sslHandler.handshakeFuture().addListener(new FutureListener() { - @Override - public void operationComplete(Future future) throws Exception { - if (future.isSuccess()) { - LOG.trace("SSL Handshake has completed: {}", channel); - if (!options.isUseWebSockets()) { - handleConnected(channel); - } - } else { - LOG.trace("SSL Handshake has failed: {}", channel); - handleTransportFailure(channel, future.cause()); - } - } - }); - } - } - - @Override - public void channelInactive(ChannelHandlerContext context) throws Exception { - if (handshakeTimeoutFuture != null) { - handshakeTimeoutFuture.cancel(); - } - - handleTransportFailure(context.channel(), new IOException("Remote closed connection unexpectedly")); - } - - @Override - public void channelExceptionCaught(ChannelHandlerContext context, Throwable cause) throws Exception { - handleTransportFailure(context.channel(), cause); - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object message) { - if (options.isUseWebSockets()) { - LOG.trace("New data read: incoming: {}", message); - - Channel ch = ctx.channel(); - if (!handshaker.isHandshakeComplete()) { - handshaker.finishHandshake(ch, (FullHttpResponse) message); - LOG.trace("WebSocket Client connected! {}", ctx.channel()); - // Now trigger super processing as we are really connected. - if (handshakeTimeoutFuture.cancel()) { - handleConnected(ch); - } - - return; - } - - // We shouldn't get this since we handle the handshake previously. - if (message instanceof FullHttpResponse) { - FullHttpResponse response = (FullHttpResponse) message; - throw new IllegalStateException( - "Unexpected FullHttpResponse (getStatus=" + response.status() + - ", content=" + response.payload().toString(StandardCharsets.UTF_8) + ')'); - } - - WebSocketFrame frame = (WebSocketFrame) message; - if (frame instanceof TextWebSocketFrame) { - TextWebSocketFrame textFrame = (TextWebSocketFrame) frame; - LOG.warn("WebSocket Client received message: " + textFrame.text()); - ctx.fireChannelExceptionCaught(new IOException("Received invalid frame over WebSocket.")); - } else if (frame instanceof BinaryWebSocketFrame) { - BinaryWebSocketFrame binaryFrame = (BinaryWebSocketFrame) frame; - LOG.trace("WebSocket Client received data: {} bytes", binaryFrame.binaryData().readableBytes()); - ctx.fireChannelRead(binaryFrame.binaryData()); - } else if (frame instanceof ContinuationWebSocketFrame) { - ContinuationWebSocketFrame continuationFrame = (ContinuationWebSocketFrame) frame; - LOG.trace("WebSocket Client received data continuation: {} bytes", continuationFrame.binaryData().readableBytes()); - ctx.fireChannelRead(continuationFrame.binaryData()); - } else if (frame instanceof PingWebSocketFrame) { - LOG.trace("WebSocket Client received ping, response with pong"); - ch.write(new PongWebSocketFrame(frame.binaryData())); - } else if (frame instanceof CloseWebSocketFrame) { - LOG.trace("WebSocket Client received closing"); - ch.close(); - } - } else { - ctx.fireChannelRead(message); - } - } - } - - private class NettyClientOutboundHandler extends ChannelHandlerAdapter { - - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - LOG.trace("NettyServerHandler: Channel write: {}", msg); - if (options.isUseWebSockets() && msg instanceof Buffer) { - if (options.isFragmentWrites()) { - Buffer orig = (Buffer) msg; - int origIndex = orig.readerOffset(); - int split = orig.readableBytes()/2; - - Buffer part1 = orig.copy(origIndex, split); - LOG.trace("NettyClientOutboundHandler: Part1: {}", part1); - orig.readerOffset(origIndex + split); - LOG.trace("NettyClientOutboundHandler: Part2: {}", orig); - - BinaryWebSocketFrame frame1 = new BinaryWebSocketFrame(false, 0, part1); - ctx.writeAndFlush(frame1); - ContinuationWebSocketFrame frame2 = new ContinuationWebSocketFrame(true, 0, orig); - return ctx.write(frame2); - } else { - BinaryWebSocketFrame frame = new BinaryWebSocketFrame((Buffer) msg); - return ctx.write(frame); - } - } else { - return ctx.write(msg); - } - } - } - - private class ClientWSCompressionObserverHandler extends ChannelHandlerAdapter { - - final String WS_EXTENSIONS_SECTION = "sec-websocket-extensions"; - final String WS_PERMESSAGE_DEFLATE = "permessage-deflate"; - final String WS_UPGRADE = "upgrade"; - - @Override - public void channelRead(ChannelHandlerContext ctx, Object message) { - if (message instanceof FullHttpResponse) { - FullHttpResponse response = (FullHttpResponse) message; - HttpHeaders headers = response.headers(); - - if (headers.contains(WS_UPGRADE) && headers.contains(WS_EXTENSIONS_SECTION)) { - wsCompressionRequest = headers.get(WS_EXTENSIONS_SECTION).toString().contains(WS_PERMESSAGE_DEFLATE); - } - } - - ctx.fireChannelRead(message); - } - - @Override - public Future write(ChannelHandlerContext context, Object message) { - if (message instanceof FullHttpRequest) { - FullHttpRequest request = (FullHttpRequest) message; - HttpHeaders headers = request.headers(); - - if (headers.contains(WS_UPGRADE) && headers.contains(WS_EXTENSIONS_SECTION)) { - wsCompressionRequest = headers.get(WS_EXTENSIONS_SECTION).toString().contains(WS_PERMESSAGE_DEFLATE); - } - } - - return context.write(message); - } - } - - //----- Internal Client implementation API - - protected ChannelHandler getClientHandler() { - return new SimpleChannelInboundHandler() { - - @Override - protected void messageReceived(ChannelHandlerContext ctx, Buffer input) throws Exception { - LOG.trace("AMQP Test Client Channel read: {}", input); - - // Driver processes new data and may produce output based on this. - try { - final ByteBuffer copy = ByteBuffer.allocate(input.readableBytes()); - input.readBytes(copy); - inputConsumer.accept(copy.flip().asReadOnlyBuffer()); - } catch (Throwable e) { - LOG.error("Closed AMQP Test client channel due to error: ", e); - ctx.channel().close(); - } - } - }; - } - - protected EventLoop getEventLoop() { - if (channel == null || !channel.isActive()) { - throw new IllegalStateException("Channel is not connected or has closed"); - } - - return channel.executor(); - } - - protected SslHandler getSslHandler() { - return sslHandler; - } - - private void configureChannel(final Channel channel) throws Exception { - if (isSecure()) { - final SslHandler sslHandler; - try { - sslHandler = SslSupport.createClientSslHandler(getRemoteURI(), options); - } catch (Exception ex) { - LOG.warn("Error during initialization of channel from SSL Handler creation:"); - handleTransportFailure(channel, ex); - throw new IOException(ex); - } - - channel.pipeline().addLast("ssl", sslHandler); - } - - if (options.isTraceBytes()) { - channel.pipeline().addLast("logger", new LoggingHandler(getClass())); - } - - if (options.isUseWebSockets()) { - channel.pipeline().addLast(new HttpClientCodec()); - channel.pipeline().addLast(new HttpObjectAggregator(8192)); - if (options.isWebSocketCompression()) { - channel.pipeline().addLast(new ClientWSCompressionObserverHandler()); - channel.pipeline().addLast(WebSocketClientCompressionHandler.INSTANCE); - } - } - - channel.pipeline().addLast(new NettyClientOutboundHandler()); - channel.pipeline().addLast(new NettyClientInboundHandler()); - channel.pipeline().addLast(getClientHandler()); - } - - private void configureNetty(Bootstrap bootstrap, ProtonTestClientOptions options) { - bootstrap.option(ChannelOption.TCP_NODELAY, options.isTcpNoDelay()); - bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, options.getConnectTimeout()); - bootstrap.option(ChannelOption.SO_KEEPALIVE, options.isTcpKeepAlive()); - bootstrap.option(ChannelOption.SO_LINGER, options.getSoLinger()); - - if (options.getSendBufferSize() != -1) { - bootstrap.option(ChannelOption.SO_SNDBUF, options.getSendBufferSize()); - } - - if (options.getReceiveBufferSize() != -1) { - bootstrap.option(ChannelOption.SO_RCVBUF, options.getReceiveBufferSize()); - } - - if (options.getTrafficClass() != -1) { - bootstrap.option(ChannelOption.IP_TOS, options.getTrafficClass()); - } - - if (options.getLocalAddress() != null || options.getLocalPort() != 0) { - if (options.getLocalAddress() != null) { - bootstrap.localAddress(options.getLocalAddress(), options.getLocalPort()); - } else { - bootstrap.localAddress(options.getLocalPort()); - } - } - } - - //----- Event Handlers which can be overridden in subclasses -------------// - - protected void handleConnected(Channel connectedChannel) { - LOG.trace("Channel has become active! Channel is {}", connectedChannel); - channel = connectedChannel; - connected.set(true); - connectedLatch.countDown(); - connectedRunnable.run(); - } - - protected void handleTransportFailure(Channel failedChannel, Throwable cause) { - if (!closed.get()) { - LOG.trace("Channel indicates connection failure! Channel is {}", failedChannel); - failureCause = new IOException(cause); - channel = failedChannel; - connected.set(false); - connectedLatch.countDown(); - } else { - LOG.trace("Closed Channel signalled that the channel ended: {}", channel); - } - } -} diff --git a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/netty/netty5/Netty5EventLoop.java b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/netty/netty5/Netty5EventLoop.java deleted file mode 100644 index 16056003..00000000 --- a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/netty/netty5/Netty5EventLoop.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.qpid.protonj2.test.driver.netty.netty5; - -import java.util.concurrent.TimeUnit; - -import org.apache.qpid.protonj2.test.driver.netty.NettyEventLoop; - -import io.netty5.channel.EventLoop; - -public final class Netty5EventLoop implements NettyEventLoop { - - private final EventLoop loop; - - public Netty5EventLoop(EventLoop loop) { - this.loop = loop; - } - - @Override - public boolean inEventLoop() { - return loop.inEventLoop(); - } - - @Override - public void execute(Runnable runnable) { - loop.execute(runnable); - } - - @Override - public void schedule(Runnable runnable, int delay, TimeUnit unit) { - loop.schedule(runnable, delay, unit); - } -} diff --git a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/netty/netty5/Netty5Server.java b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/netty/netty5/Netty5Server.java deleted file mode 100644 index 4e08ba49..00000000 --- a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/netty/netty5/Netty5Server.java +++ /dev/null @@ -1,580 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.qpid.protonj2.test.driver.netty.netty5; - -import java.lang.invoke.MethodHandles; -import java.net.InetSocketAddress; -import java.net.URI; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.Objects; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Consumer; - -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLPeerUnverifiedException; - -import org.apache.qpid.protonj2.test.driver.ProtonTestServerOptions; -import org.apache.qpid.protonj2.test.driver.netty.NettyEventLoop; -import org.apache.qpid.protonj2.test.driver.netty.NettyServer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.netty5.bootstrap.ServerBootstrap; -import io.netty5.buffer.Buffer; -import io.netty5.buffer.BufferAllocator; -import io.netty5.buffer.DefaultBufferAllocators; -import io.netty5.channel.Channel; -import io.netty5.channel.ChannelFutureListeners; -import io.netty5.channel.ChannelHandler; -import io.netty5.channel.ChannelHandlerAdapter; -import io.netty5.channel.ChannelHandlerContext; -import io.netty5.channel.ChannelInitializer; -import io.netty5.channel.ChannelOption; -import io.netty5.channel.EventLoopGroup; -import io.netty5.channel.MultithreadEventLoopGroup; -import io.netty5.channel.SimpleChannelInboundHandler; -import io.netty5.channel.nio.NioHandler; -import io.netty5.channel.socket.nio.NioServerSocketChannel; -import io.netty5.handler.codec.http.DefaultFullHttpResponse; -import io.netty5.handler.codec.http.DefaultHttpContent; -import io.netty5.handler.codec.http.FullHttpRequest; -import io.netty5.handler.codec.http.FullHttpResponse; -import io.netty5.handler.codec.http.HttpObjectAggregator; -import io.netty5.handler.codec.http.HttpResponseStatus; -import io.netty5.handler.codec.http.HttpServerCodec; -import io.netty5.handler.codec.http.HttpUtil; -import io.netty5.handler.codec.http.HttpVersion; -import io.netty5.handler.codec.http.headers.HttpHeaders; -import io.netty5.handler.codec.http.websocketx.BinaryWebSocketFrame; -import io.netty5.handler.codec.http.websocketx.ContinuationWebSocketFrame; -import io.netty5.handler.codec.http.websocketx.WebSocketFrame; -import io.netty5.handler.codec.http.websocketx.WebSocketServerHandshakeCompletionEvent; -import io.netty5.handler.codec.http.websocketx.WebSocketServerProtocolHandler; -import io.netty5.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler; -import io.netty5.handler.logging.LogLevel; -import io.netty5.handler.logging.LoggingHandler; -import io.netty5.handler.ssl.SslHandler; -import io.netty5.util.concurrent.Future; -import io.netty5.util.concurrent.FutureListener; - -/** - * Base Server implementation used to create Netty based server implementations for - * unit testing aspects of the client code. - */ -public final class Netty5Server implements NettyServer { - - private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - - static final int PORT = Integer.parseInt(System.getProperty("port", "5672")); - static final String WEBSOCKET_PATH = "/"; - static final int DEFAULT_MAX_FRAME_SIZE = 65535; - - private Netty5EventLoop eventLoop; - private EventLoopGroup bossGroup; - private EventLoopGroup workerGroup; - private Channel serverChannel; - private Channel clientChannel; - private final ProtonTestServerOptions options; - private int maxFrameSize = DEFAULT_MAX_FRAME_SIZE; - private String webSocketPath = WEBSOCKET_PATH; - private boolean wsCompressionRequest; - private boolean wsCompressionResponse; - private volatile SslHandler sslHandler; - private volatile WebSocketServerHandshakeCompletionEvent handshakeComplete; - private final CountDownLatch handshakeCompletion = new CountDownLatch(1); - - private final AtomicBoolean started = new AtomicBoolean(); - - private final Consumer inputConsumer; - private final Runnable connectedRunnable; - private final Runnable disconnectedRunnable; - - public Netty5Server(ProtonTestServerOptions options, Runnable connectedRunnable, Runnable disconnectedRunnable, Consumer inputConsumer) { - Objects.requireNonNull(options); - Objects.requireNonNull(inputConsumer); - Objects.requireNonNull(connectedRunnable); - - this.options = options; - this.connectedRunnable = connectedRunnable; - this.disconnectedRunnable = disconnectedRunnable; - this.inputConsumer = inputConsumer; - } - - @Override - public boolean isSecureServer() { - return options.isSecure(); - } - - @Override - public boolean isAcceptingConnections() { - return serverChannel != null && serverChannel.isOpen(); - } - - @Override - public boolean hasSecureConnection() { - return sslHandler != null; - } - - @Override - public boolean hasClientConnection() { - return clientChannel != null && clientChannel.isOpen(); - } - - @Override - public int getClientPort() { - Objects.requireNonNull(clientChannel); - return (((InetSocketAddress) clientChannel.remoteAddress()).getPort()); - } - - @Override - public boolean isWSCompressionActive() { - Objects.requireNonNull(clientChannel); - return wsCompressionRequest && wsCompressionResponse; - } - - @Override - public boolean isPeerVerified() { - try { - if (hasSecureConnection()) { - return sslHandler.engine().getSession().getPeerPrincipal() != null; - } else { - return false; - } - } catch (SSLPeerUnverifiedException unverified) { - return false; - } - } - - @Override - public SSLEngine getConnectionSSLEngine() { - if (hasSecureConnection()) { - return sslHandler.engine(); - } else { - return null; - } - } - - @Override - public boolean isWebSocketServer() { - return options.isUseWebSockets(); - } - - @Override - public String getWebSocketPath() { - return webSocketPath; - } - - @Override - public void setWebSocketPath(String webSocketPath) { - this.webSocketPath = webSocketPath; - } - - @Override - public int getMaxFrameSize() { - return maxFrameSize; - } - - @Override - public void setMaxFrameSize(int maxFrameSize) { - this.maxFrameSize = maxFrameSize; - } - - public boolean awaitHandshakeCompletion(long delayMs) throws InterruptedException { - return handshakeCompletion.await(delayMs, TimeUnit.MILLISECONDS); - } - - public WebSocketServerHandshakeCompletionEvent getHandshakeComplete() { - return handshakeComplete; - } - - @Override - public URI getConnectionURI(String queryString) throws Exception { - if (!started.get()) { - throw new IllegalStateException("Cannot get URI of non-started server"); - } - - int port = getServerPort(); - - String scheme; - String path; - - if (isWebSocketServer()) { - if (isSecureServer()) { - scheme = "amqpwss"; - } else { - scheme = "amqpws"; - } - } else { - if (isSecureServer()) { - scheme = "amqps"; - } else { - scheme = "amqp"; - } - } - - if (isWebSocketServer()) { - path = getWebSocketPath(); - } else { - path = null; - } - - if (queryString != null && queryString.startsWith("?")) { - queryString = queryString.substring(1); - } - - return new URI(scheme, null, "localhost", port, path, queryString, null); - } - - @Override - public void start() throws Exception { - if (started.compareAndSet(false, true)) { - // Configure the server to basic NIO type channels - bossGroup = new MultithreadEventLoopGroup(1, NioHandler.newFactory()); - workerGroup = new MultithreadEventLoopGroup(NioHandler.newFactory()); - - ServerBootstrap server = new ServerBootstrap(); - server.group(bossGroup, workerGroup); - server.channel(NioServerSocketChannel.class); - server.option(ChannelOption.SO_BACKLOG, 100); - server.handler(new LoggingHandler(LogLevel.INFO)); - server.childHandler(new ChannelInitializer() { - - @Override - public void initChannel(Channel ch) throws Exception { - // Don't accept any new connections. - serverChannel.close(); - // Now we know who the client is - clientChannel = ch; - eventLoop = new Netty5EventLoop(ch.executor()); - - if (isSecureServer()) { - ch.pipeline().addLast(sslHandler = SslSupport.createServerSslHandler(null, options)); - } - - if (options.isUseWebSockets()) { - ch.pipeline().addLast(new HttpServerCodec()); - ch.pipeline().addLast(new HttpObjectAggregator(65536)); - if (options.isWebSocketCompression()) { - ch.pipeline().addLast(new ServerWSCompressionObserverHandler()); - ch.pipeline().addLast(new WebSocketServerCompressionHandler()); - } - ch.pipeline().addLast(new WebSocketServerProtocolHandler(getWebSocketPath(), "amqp", true, maxFrameSize)); - } - - ch.pipeline().addLast(new NettyServerOutboundHandler()); - ch.pipeline().addLast(new NettyServerInboundHandler()); - ch.pipeline().addLast(getServerHandler()); - } - }); - - // Start the server and then update the server port in case the configuration - // was such that the server chose a free port. - serverChannel = server.bind(options.getServerPort()).asStage().get(); - options.setServerPort(((InetSocketAddress) serverChannel.localAddress()).getPort()); - } - } - - protected ChannelHandler getServerHandler() { - return new SimpleChannelInboundHandler() { - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - connectedRunnable.run(); - ctx.fireChannelActive(); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - disconnectedRunnable.run(); - ctx.fireChannelInactive(); - } - - @Override - protected void messageReceived(ChannelHandlerContext ctx, Buffer input) throws Exception { - LOG.trace("AMQP Test Server Channel read: {}", input); - - // Driver processes new data and may produce output based on this. - try { - final ByteBuffer copy = ByteBuffer.allocate(input.readableBytes()); - input.readBytes(copy); - inputConsumer.accept(copy.flip().asReadOnlyBuffer()); - } catch (Throwable e) { - LOG.error("Closed AMQP Test server channel due to error: ", e); - ctx.channel().close(); - } - } - }; - } - - @Override - public void write(ByteBuffer frame) { - if (clientChannel == null || !clientChannel.isActive()) { - throw new IllegalStateException("Channel is not connected or has closed"); - } - - clientChannel.writeAndFlush(BufferAllocator.onHeapUnpooled().copyOf(frame).makeReadOnly()); - } - - @Override - public NettyEventLoop eventLoop() { - if (clientChannel == null || !clientChannel.isActive()) { - throw new IllegalStateException("Channel is not connected or has closed"); - } - - return eventLoop; - } - - @Override - public void stop() throws InterruptedException { - if (started.compareAndSet(true, false)) { - LOG.info("Syncing channel close"); - serverChannel.close().asStage().sync(); - - if (clientChannel != null) { - try { - if (!clientChannel.close().asStage().await(10, TimeUnit.SECONDS)) { - LOG.info("Connected Client channel close timed out waiting for result"); - } - } catch (InterruptedException e) { - Thread.interrupted(); - LOG.debug("Close of connected client channel interrupted while awaiting result"); - } - } - - // Shut down all event loops to terminate all threads. - int timeout = 100; - LOG.trace("Shutting down boss group"); - bossGroup.shutdownGracefully(0, timeout, TimeUnit.MILLISECONDS).asStage().await(timeout, TimeUnit.MILLISECONDS); - LOG.trace("Boss group shut down"); - - LOG.trace("Shutting down worker group"); - workerGroup.shutdownGracefully(0, timeout, TimeUnit.MILLISECONDS).asStage().await(timeout, TimeUnit.MILLISECONDS); - LOG.trace("Worker group shut down"); - } - } - - @Override - public void stopAsync() throws InterruptedException { - if (started.compareAndSet(true, false)) { - LOG.info("Closing channel asynchronously"); - serverChannel.close().asStage().sync(); - - if (clientChannel != null) { - clientChannel.close(); - } - - // Shut down all event loops to terminate all threads. - int timeout = 100; - LOG.trace("Shutting down boss group asynchronously"); - bossGroup.shutdownGracefully(0, timeout, TimeUnit.MILLISECONDS); - - LOG.trace("Shutting down worker group asynchronously"); - workerGroup.shutdownGracefully(0, timeout, TimeUnit.MILLISECONDS); - } - } - - @Override - public void close() throws InterruptedException { - stop(); - } - - @Override - public void disconnectClient() throws Exception { - if (!started.get() || !serverChannel.isOpen()) { - throw new IllegalStateException("Server must be currently active in order to reset"); - } - - if (clientChannel != null) { - try { - if (!clientChannel.close().asStage().await(10, TimeUnit.SECONDS)) { - LOG.info("Connected Client channel close timed out waiting for result"); - } - } catch (InterruptedException e) { - Thread.interrupted(); - LOG.debug("Close of connected client channel interrupted while awaiting result"); - } finally { - clientChannel = null; - } - } - } - - @Override - public int getServerPort() { - if (!started.get()) { - throw new IllegalStateException("Cannot get server port of non-started server"); - } - - return options.getServerPort(); - } - - private class NettyServerOutboundHandler extends ChannelHandlerAdapter { - - @Override - public Future write(ChannelHandlerContext ctx, Object msg) { - LOG.trace("NettyServerHandler: Channel write: {}", msg); - if (isWebSocketServer() && msg instanceof Buffer) { - if (options.isFragmentWrites()) { - Buffer orig = (Buffer) msg; - int origIndex = orig.readerOffset(); - int split = orig.readableBytes()/2; - - Buffer part1 = orig.copy(origIndex, split); - LOG.trace("NettyServerHandler: Part1: {}", part1); - orig.readerOffset(origIndex + split); - LOG.trace("NettyServerHandler: Part2: {}", orig); - - BinaryWebSocketFrame frame1 = new BinaryWebSocketFrame(false, 0, part1); - ctx.writeAndFlush(frame1); - ContinuationWebSocketFrame frame2 = new ContinuationWebSocketFrame(true, 0, orig); - return ctx.write(frame2); - } else { - BinaryWebSocketFrame frame = new BinaryWebSocketFrame((Buffer) msg); - return ctx.write(frame); - } - } else { - return ctx.write(msg); - } - } - } - - private class NettyServerInboundHandler extends ChannelHandlerAdapter { - - @Override - public void channelInboundEvent(ChannelHandlerContext context, Object payload) { - if (payload instanceof WebSocketServerHandshakeCompletionEvent) { - handshakeComplete = (WebSocketServerHandshakeCompletionEvent) payload; - handshakeCompletion.countDown(); - } - } - - @Override - public void channelActive(final ChannelHandlerContext ctx) { - LOG.info("NettyServerHandler -> New active channel: {}", ctx.channel()); - SslHandler handler = ctx.pipeline().get(SslHandler.class); - if (handler != null) { - handler.handshakeFuture().addListener(new FutureListener() { - @Override - public void operationComplete(Future future) throws Exception { - LOG.info("Server -> SSL handshake completed. Succeeded: {}", future.isSuccess()); - if (!future.isSuccess()) { - ctx.close(); - } - } - }); - } - - ctx.fireChannelActive(); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - LOG.info("NettyServerHandler: channel has gone inactive: {}", ctx.channel()); - ctx.close(); - ctx.fireChannelInactive(); - Netty5Server.this.clientChannel = null; - } - - @Override - public void channelRead(ChannelHandlerContext ctx, Object msg) { - LOG.trace("NettyServerHandler: Channel read: {}", msg); - if (msg instanceof WebSocketFrame) { - WebSocketFrame frame = (WebSocketFrame) msg; - ctx.fireChannelRead(frame.binaryData()); - } else if (msg instanceof FullHttpRequest) { - // Reject anything not on the WebSocket path - FullHttpRequest request = (FullHttpRequest) msg; - - sendHttpResponse(ctx, request, - new DefaultFullHttpResponse( - HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST, DefaultBufferAllocators.onHeapAllocator().allocate(0))); - } else { - // Forward anything else along to the next handler. - ctx.fireChannelRead(msg); - } - } - - @Override - public void channelReadComplete(ChannelHandlerContext ctx) { - ctx.flush(); - } - - @Override - public void channelExceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - LOG.info("NettyServerHandler: NettyServerHandlerException caught on channel: {}", ctx.channel()); - // Close the connection when an exception is raised. - cause.printStackTrace(); - ctx.close(); - } - } - - private class ServerWSCompressionObserverHandler extends ChannelHandlerAdapter { - - final String WS_EXTENSIONS_SECTION = "sec-websocket-extensions"; - final String WS_PERMESSAGE_DEFLATE = "permessage-deflate"; - final String WS_UPGRADE = "upgrade"; - - @Override - public void channelRead(ChannelHandlerContext ctx, Object message) { - if (message instanceof FullHttpRequest) { - FullHttpRequest request = (FullHttpRequest) message; - HttpHeaders headers = request.headers(); - - if (headers.contains(WS_UPGRADE) && headers.contains(WS_EXTENSIONS_SECTION)) { - wsCompressionRequest = headers.get(WS_EXTENSIONS_SECTION).toString().contains(WS_PERMESSAGE_DEFLATE); - } - } - - ctx.fireChannelRead(message); - } - - @Override - public Future write(ChannelHandlerContext context, Object message) { - if (message instanceof FullHttpResponse) { - FullHttpResponse response = (FullHttpResponse) message; - HttpHeaders headers = response.headers(); - - if (headers.contains(WS_UPGRADE) && headers.contains(WS_EXTENSIONS_SECTION)) { - wsCompressionResponse = headers.get(WS_EXTENSIONS_SECTION).toString().contains(WS_PERMESSAGE_DEFLATE); - } - } - - return context.write(message); - } - } - - private static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest request, FullHttpResponse response) { - // Generate an error page if response getStatus code is not OK (200). - if (response.status().code() != 200) { - byte[] status = response.status().toString().getBytes(StandardCharsets.UTF_8); - response.payload().writeBytes(status); - HttpUtil.setContentLength(response, response.payload().readableBytes()); - } - - // Send the response and close the connection if necessary. - Future f = ctx.channel().writeAndFlush(response); - if (!HttpUtil.isKeepAlive(request) || response.status().code() != 200) { - f.addListener(ctx.channel(), ChannelFutureListeners.CLOSE); - } - } - - protected SslHandler getSslHandler() { - return sslHandler; - } -} diff --git a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/netty/netty5/Netty5Support.java b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/netty/netty5/Netty5Support.java deleted file mode 100644 index 776744fd..00000000 --- a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/netty/netty5/Netty5Support.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.qpid.protonj2.test.driver.netty.netty5; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.netty5.buffer.BufferAllocator; - -/** - * Support class used to detect if Netty 5 is available on the class path. - */ -public final class Netty5Support { - - private static final Logger LOG = LoggerFactory.getLogger(Netty5Support.class); - - private static final Throwable UNAVAILABILITY_CAUSE; - static { - Throwable cause = null; - try { - BufferAllocator.onHeapUnpooled(); - } catch (Throwable ex) { - LOG.debug("Netty 5 not available for use."); - cause = ex; - } - - UNAVAILABILITY_CAUSE = cause; - } - - public static final boolean isAvailable() { - return UNAVAILABILITY_CAUSE == null; - } -} diff --git a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/netty/netty5/SslSupport.java b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/netty/netty5/SslSupport.java deleted file mode 100644 index 5fbdcae2..00000000 --- a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/netty/netty5/SslSupport.java +++ /dev/null @@ -1,498 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.qpid.protonj2.test.driver.netty.netty5; - -import java.io.File; -import java.io.FileInputStream; -import java.io.InputStream; -import java.lang.invoke.MethodHandles; -import java.net.URI; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLParameters; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509ExtendedKeyManager; - -import org.apache.qpid.protonj2.test.driver.ProtonTestClientOptions; -import org.apache.qpid.protonj2.test.driver.ProtonTestServerOptions; -import org.apache.qpid.protonj2.test.driver.netty.X509AliasKeyManager; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.netty5.handler.ssl.SslHandler; -import io.netty5.handler.ssl.util.InsecureTrustManagerFactory; - -/** - * Static class that provides various utility methods used by Transport implementations. - */ -public class SslSupport { - - private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - - /** - * Creates a Netty SslHandler instance for use in client instances that require - * an SSL encoder / decoder. - * - * If the given options contain an SSLContext override, this will be used directly - * when creating the handler. If they do not, an SSLContext will first be created - * using the other option values. - * - * @param remote - * The URI of the remote peer that the SslHandler will be used against. - * @param options - * The SSL options object to build the SslHandler instance from. - * - * @return a new SslHandler that is configured from the given options. - * - * @throws Exception if an error occurs while creating the SslHandler instance. - */ - public static SslHandler createClientSslHandler(URI remote, ProtonTestClientOptions options) throws Exception { - final SSLEngine sslEngine; - - SSLContext sslContext = options.getSslContextOverride(); - if (sslContext == null) { - sslContext = createClientJdkSslContext(options); - } - - sslEngine = createClientJdkSslEngine(remote, sslContext, options); - - return new SslHandler(sslEngine); - } - - /** - * Creates a Netty SslHandler instance for use in server instances that require - * an SSL encoder / decoder. - * - * If the given options contain an SSLContext override, this will be used directly - * when creating the handler. If they do not, an SSLContext will first be created - * using the other option values. - * - * @param remote - * The URI of the remote peer that the SslHandler will be used against. - * @param options - * The SSL options object to build the SslHandler instance from. - * - * @return a new SslHandler that is configured from the given options. - * - * @throws Exception if an error occurs while creating the SslHandler instance. - */ - public static SslHandler createServerSslHandler(URI remote, ProtonTestServerOptions options) throws Exception { - final SSLEngine sslEngine; - - SSLContext sslContext = options.getSslContextOverride(); - if (sslContext == null) { - sslContext = createServerJdkSslContext(options); - } - - sslEngine = createServerJdkSslEngine(remote, sslContext, options); - - return new SslHandler(sslEngine); - } - - //----- JDK SSL Support Methods ------------------------------------------// - - /** - * Create a new SSLContext using the options specific in the given TransportOptions - * instance. - * - * @param options - * the configured options used to create the SSLContext. - * - * @return a new SSLContext instance. - * - * @throws Exception if an error occurs while creating the context. - */ - public static SSLContext createClientJdkSslContext(ProtonTestClientOptions options) throws Exception { - try { - String contextProtocol = options.getContextProtocol(); - LOG.trace("Getting SSLContext instance using protocol: {}", contextProtocol); - - SSLContext context = SSLContext.getInstance(contextProtocol); - - KeyManager[] keyMgrs = loadKeyManagers(options); - TrustManager[] trustManagers = loadTrustManagers(options); - - context.init(keyMgrs, trustManagers, new SecureRandom()); - return context; - } catch (Exception e) { - LOG.error("Failed to create SSLContext: {}", e, e); - throw e; - } - } - - /** - * Create a new SSLContext using the options specific in the given TransportOptions - * instance. - * - * @param options - * the configured options used to create the SSLContext. - * - * @return a new SSLContext instance. - * - * @throws Exception if an error occurs while creating the context. - */ - public static SSLContext createServerJdkSslContext(ProtonTestServerOptions options) throws Exception { - try { - String contextProtocol = options.getContextProtocol(); - LOG.trace("Getting SSLContext instance using protocol: {}", contextProtocol); - - SSLContext context = SSLContext.getInstance(contextProtocol); - - KeyManager[] keyMgrs = loadKeyManagers(options); - TrustManager[] trustManagers = loadTrustManagers(options); - - context.init(keyMgrs, trustManagers, new SecureRandom()); - return context; - } catch (Exception e) { - LOG.error("Failed to create SSLContext: {}", e, e); - throw e; - } - } - - /** - * Create a new JDK SSLEngine instance in client mode from the given SSLContext and - * TransportOptions instances. - * - * @param remote - * the URI of the remote peer that will be used to initialize the engine, may be null if none should. - * @param context - * the SSLContext to use when creating the engine. - * @param options - * the TransportOptions to use to configure the new SSLEngine. - * - * @return a new SSLEngine instance in client mode. - * - * @throws Exception if an error occurs while creating the new SSLEngine. - */ - public static SSLEngine createClientJdkSslEngine(URI remote, SSLContext context, ProtonTestClientOptions options) throws Exception { - SSLEngine engine = null; - if (remote == null) { - engine = context.createSSLEngine(); - } else { - engine = context.createSSLEngine(remote.getHost(), remote.getPort()); - } - - engine.setEnabledProtocols(buildEnabledProtocols(engine, options)); - engine.setEnabledCipherSuites(buildEnabledCipherSuites(engine, options)); - engine.setUseClientMode(true); - engine.setNeedClientAuth(options.isNeedClientAuth()); - - if (options.isVerifyHost()) { - SSLParameters sslParameters = engine.getSSLParameters(); - sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); - engine.setSSLParameters(sslParameters); - } - - return engine; - } - - /** - * Create a new JDK SSLEngine instance in client mode from the given SSLContext and - * TransportOptions instances. - * - * @param remote - * the URI of the remote peer that will be used to initialize the engine, may be null if none should. - * @param context - * the SSLContext to use when creating the engine. - * @param options - * the TransportOptions to use to configure the new SSLEngine. - * - * @return a new SSLEngine instance in client mode. - * - * @throws Exception if an error occurs while creating the new SSLEngine. - */ - public static SSLEngine createServerJdkSslEngine(URI remote, SSLContext context, ProtonTestServerOptions options) throws Exception { - SSLEngine engine = null; - if (remote == null) { - engine = context.createSSLEngine(); - } else { - engine = context.createSSLEngine(remote.getHost(), remote.getPort()); - } - - engine.setEnabledProtocols(buildEnabledProtocols(engine, options)); - engine.setEnabledCipherSuites(buildEnabledCipherSuites(engine, options)); - engine.setUseClientMode(false); - engine.setNeedClientAuth(options.isNeedClientAuth()); - - if (options.isVerifyHost()) { - SSLParameters sslParameters = engine.getSSLParameters(); - sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); - engine.setSSLParameters(sslParameters); - } - - return engine; - } - - //----- Internal support methods -----------------------------------------// - - private static String[] buildEnabledProtocols(SSLEngine engine, ProtonTestClientOptions options) { - List enabledProtocols = new ArrayList<>(); - - if (options.getEnabledProtocols() != null) { - List configuredProtocols = Arrays.asList(options.getEnabledProtocols()); - LOG.trace("Configured protocols from transport options: {}", configuredProtocols); - enabledProtocols.addAll(configuredProtocols); - } else { - List engineProtocols = Arrays.asList(engine.getEnabledProtocols()); - LOG.trace("Default protocols from the SSLEngine: {}", engineProtocols); - enabledProtocols.addAll(engineProtocols); - } - - String[] disabledProtocols = options.getDisabledProtocols(); - if (disabledProtocols != null) { - List disabled = Arrays.asList(disabledProtocols); - LOG.trace("Disabled protocols: {}", disabled); - enabledProtocols.removeAll(disabled); - } - - LOG.trace("Enabled protocols: {}", enabledProtocols); - - return enabledProtocols.toArray(new String[0]); - } - - private static String[] buildEnabledProtocols(SSLEngine engine, ProtonTestServerOptions options) { - List enabledProtocols = new ArrayList<>(); - - if (options.getEnabledProtocols() != null) { - List configuredProtocols = Arrays.asList(options.getEnabledProtocols()); - LOG.trace("Configured protocols from transport options: {}", configuredProtocols); - enabledProtocols.addAll(configuredProtocols); - } else { - List engineProtocols = Arrays.asList(engine.getEnabledProtocols()); - LOG.trace("Default protocols from the SSLEngine: {}", engineProtocols); - enabledProtocols.addAll(engineProtocols); - } - - String[] disabledProtocols = options.getDisabledProtocols(); - if (disabledProtocols != null) { - List disabled = Arrays.asList(disabledProtocols); - LOG.trace("Disabled protocols: {}", disabled); - enabledProtocols.removeAll(disabled); - } - - LOG.trace("Enabled protocols: {}", enabledProtocols); - - return enabledProtocols.toArray(new String[0]); - } - - private static String[] buildEnabledCipherSuites(SSLEngine engine, ProtonTestServerOptions options) { - List enabledCipherSuites = new ArrayList<>(); - - if (options.getEnabledCipherSuites() != null) { - List configuredCipherSuites = Arrays.asList(options.getEnabledCipherSuites()); - LOG.trace("Configured cipher suites from transport options: {}", configuredCipherSuites); - enabledCipherSuites.addAll(configuredCipherSuites); - } else { - List engineCipherSuites = Arrays.asList(engine.getEnabledCipherSuites()); - LOG.trace("Default cipher suites from the SSLEngine: {}", engineCipherSuites); - enabledCipherSuites.addAll(engineCipherSuites); - } - - String[] disabledCipherSuites = options.getDisabledCipherSuites(); - if (disabledCipherSuites != null) { - List disabled = Arrays.asList(disabledCipherSuites); - LOG.trace("Disabled cipher suites: {}", disabled); - enabledCipherSuites.removeAll(disabled); - } - - LOG.trace("Enabled cipher suites: {}", enabledCipherSuites); - - return enabledCipherSuites.toArray(new String[0]); - } - - private static String[] buildEnabledCipherSuites(SSLEngine engine, ProtonTestClientOptions options) { - List enabledCipherSuites = new ArrayList<>(); - - if (options.getEnabledCipherSuites() != null) { - List configuredCipherSuites = Arrays.asList(options.getEnabledCipherSuites()); - LOG.trace("Configured cipher suites from transport options: {}", configuredCipherSuites); - enabledCipherSuites.addAll(configuredCipherSuites); - } else { - List engineCipherSuites = Arrays.asList(engine.getEnabledCipherSuites()); - LOG.trace("Default cipher suites from the SSLEngine: {}", engineCipherSuites); - enabledCipherSuites.addAll(engineCipherSuites); - } - - String[] disabledCipherSuites = options.getDisabledCipherSuites(); - if (disabledCipherSuites != null) { - List disabled = Arrays.asList(disabledCipherSuites); - LOG.trace("Disabled cipher suites: {}", disabled); - enabledCipherSuites.removeAll(disabled); - } - - LOG.trace("Enabled cipher suites: {}", enabledCipherSuites); - - return enabledCipherSuites.toArray(new String[0]); - } - - private static TrustManager[] loadTrustManagers(ProtonTestClientOptions options) throws Exception { - TrustManagerFactory factory = loadTrustManagerFactory(options); - if (factory != null) { - return factory.getTrustManagers(); - } else { - return null; - } - } - - private static TrustManager[] loadTrustManagers(ProtonTestServerOptions options) throws Exception { - TrustManagerFactory factory = loadTrustManagerFactory(options); - if (factory != null) { - return factory.getTrustManagers(); - } else { - return null; - } - } - - private static TrustManagerFactory loadTrustManagerFactory(ProtonTestClientOptions options) throws Exception { - if (options.isTrustAll()) { - return InsecureTrustManagerFactory.INSTANCE; - } - - if (options.getTrustStoreLocation() == null) { - return null; - } - - TrustManagerFactory fact = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - - String storeLocation = options.getTrustStoreLocation(); - String storePassword = options.getTrustStorePassword(); - String storeType = options.getTrustStoreType(); - - LOG.trace("Attempt to load TrustStore from location {} of type {}", storeLocation, storeType); - - KeyStore trustStore = loadStore(storeLocation, storePassword, storeType); - fact.init(trustStore); - - return fact; - } - - private static TrustManagerFactory loadTrustManagerFactory(ProtonTestServerOptions options) throws Exception { - if (options.isTrustAll()) { - return InsecureTrustManagerFactory.INSTANCE; - } - - if (options.getTrustStoreLocation() == null) { - return null; - } - - TrustManagerFactory fact = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); - - String storeLocation = options.getTrustStoreLocation(); - String storePassword = options.getTrustStorePassword(); - String storeType = options.getTrustStoreType(); - - LOG.trace("Attempt to load TrustStore from location {} of type {}", storeLocation, storeType); - - KeyStore trustStore = loadStore(storeLocation, storePassword, storeType); - fact.init(trustStore); - - return fact; - } - - private static KeyManager[] loadKeyManagers(ProtonTestClientOptions options) throws Exception { - if (options.getKeyStoreLocation() == null) { - return null; - } - - KeyManagerFactory fact = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - - String storeLocation = options.getKeyStoreLocation(); - String storePassword = options.getKeyStorePassword(); - String storeType = options.getKeyStoreType(); - String alias = options.getKeyAlias(); - - LOG.trace("Attempt to load KeyStore from location {} of type {}", storeLocation, storeType); - - KeyStore keyStore = loadStore(storeLocation, storePassword, storeType); - fact.init(keyStore, storePassword != null ? storePassword.toCharArray() : null); - - if (alias == null) { - return fact.getKeyManagers(); - } else { - validateAlias(keyStore, alias); - return wrapKeyManagers(alias, fact.getKeyManagers()); - } - } - - private static KeyManager[] loadKeyManagers(ProtonTestServerOptions options) throws Exception { - if (options.getKeyStoreLocation() == null) { - return null; - } - - KeyManagerFactory fact = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); - - String storeLocation = options.getKeyStoreLocation(); - String storePassword = options.getKeyStorePassword(); - String storeType = options.getKeyStoreType(); - String alias = options.getKeyAlias(); - - LOG.trace("Attempt to load KeyStore from location {} of type {}", storeLocation, storeType); - - KeyStore keyStore = loadStore(storeLocation, storePassword, storeType); - fact.init(keyStore, storePassword != null ? storePassword.toCharArray() : null); - - if (alias == null) { - return fact.getKeyManagers(); - } else { - validateAlias(keyStore, alias); - return wrapKeyManagers(alias, fact.getKeyManagers()); - } - } - - private static KeyManager[] wrapKeyManagers(String alias, KeyManager[] origKeyManagers) { - KeyManager[] keyManagers = new KeyManager[origKeyManagers.length]; - for (int i = 0; i < origKeyManagers.length; i++) { - KeyManager km = origKeyManagers[i]; - if (km instanceof X509ExtendedKeyManager) { - km = new X509AliasKeyManager(alias, (X509ExtendedKeyManager) km); - } - - keyManagers[i] = km; - } - - return keyManagers; - } - - private static void validateAlias(KeyStore store, String alias) throws IllegalArgumentException, KeyStoreException { - if (!store.containsAlias(alias)) { - throw new IllegalArgumentException("The alias '" + alias + "' doesn't exist in the key store"); - } - - if (!store.isKeyEntry(alias)) { - throw new IllegalArgumentException("The alias '" + alias + "' in the keystore doesn't represent a key entry"); - } - } - - private static KeyStore loadStore(String storePath, final String password, String storeType) throws Exception { - KeyStore store = KeyStore.getInstance(storeType); - try (InputStream in = new FileInputStream(new File(storePath));) { - store.load(in, password != null ? password.toCharArray() : null); - } - - return store; - } -} diff --git a/protonj2/pom.xml b/protonj2/pom.xml index 30568aaa..c919c19d 100644 --- a/protonj2/pom.xml +++ b/protonj2/pom.xml @@ -19,7 +19,7 @@ org.apache.qpid protonj2-parent - 1.0.0-M22-SNAPSHOT + 1.0.0-M23-SNAPSHOT 4.0.0 @@ -39,11 +39,6 @@ netty-buffer provided - - io.netty - netty5-buffer - provided - org.apache.qpid proton-j diff --git a/protonj2/src/main/java/org/apache/qpid/protonj2/buffer/netty/Netty5ProtonBufferAllocator.java b/protonj2/src/main/java/org/apache/qpid/protonj2/buffer/netty/Netty5ProtonBufferAllocator.java deleted file mode 100644 index bb3d972b..00000000 --- a/protonj2/src/main/java/org/apache/qpid/protonj2/buffer/netty/Netty5ProtonBufferAllocator.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.qpid.protonj2.buffer.netty; - -import java.nio.ByteBuffer; - -import org.apache.qpid.protonj2.buffer.ProtonBuffer; -import org.apache.qpid.protonj2.buffer.ProtonBufferAllocator; -import org.apache.qpid.protonj2.buffer.ProtonCompositeBuffer; - -import io.netty5.buffer.Buffer; -import io.netty5.buffer.BufferAllocator; - -/** - * Proton managed Netty 5 {@link BufferAllocator} wrapper. - */ -public final class Netty5ProtonBufferAllocator implements ProtonBufferAllocator { - - public static int DEFAULT_CAPACITY = 1024; - - public static ProtonBufferAllocator POOLED = new Netty5ProtonBufferAllocator(BufferAllocator.onHeapPooled()); - - public static ProtonBufferAllocator UNPOOLED = new Netty5ProtonBufferAllocator(BufferAllocator.onHeapUnpooled()); - - private boolean closed; - - private final BufferAllocator allocator; - - public Netty5ProtonBufferAllocator(BufferAllocator allocator) { - this.allocator = allocator; - } - - public BufferAllocator allocator() { - return allocator; - } - - @Override - public void close() { - closed = true; - } - - /** - * Creates a {@link ProtonBuffer} wrapper around the given Netty 5 based - * Buffer instance. The method wraps a Buffer and assumes ownership of it - * which means that once the wrapper buffer is closed it will release the - * {@link Buffer} which if there are no other references will render the - * wrapped buffer closed and recyclable if pooled. Care should be take - * when supplying the buffer to add a reference depending on the source of - * the buffer. - * - * @param buffer - * The buffer instance to wrap. - * - * @return A ProtonBuffer instance that wraps the given netty buffer. - */ - public Netty5ToProtonBufferAdapter wrap(Buffer buffer) { - checkClosed(); - return new Netty5ToProtonBufferAdapter(this, buffer); - } - - @Override - public Netty5ToProtonBufferAdapter outputBuffer(int initialCapacity) { - return allocate(initialCapacity); - } - - @Override - public Netty5ToProtonBufferAdapter allocate() { - return allocate(DEFAULT_CAPACITY); - } - - @Override - public Netty5ToProtonBufferAdapter allocate(int initialCapacity) { - checkClosed(); - return new Netty5ToProtonBufferAdapter(this, allocator.allocate(initialCapacity)); - } - - @Override - public Netty5ToProtonBufferAdapter allocateHeapBuffer() { - return allocateHeapBuffer(DEFAULT_CAPACITY); - } - - @Override - public Netty5ToProtonBufferAdapter allocateHeapBuffer(int initialCapacity) { - checkClosed(); - return new Netty5ToProtonBufferAdapter(this, BufferAllocator.onHeapUnpooled().allocate(initialCapacity)); - } - - @Override - public Netty5ToProtonBufferAdapter copy(byte[] array) { - checkClosed(); - return new Netty5ToProtonBufferAdapter(this, allocator.copyOf(array)); - } - - @Override - public Netty5ToProtonBufferAdapter copy(byte[] array, int offset, int length) { - checkClosed(); - return new Netty5ToProtonBufferAdapter(this, allocator.copyOf(ByteBuffer.wrap(array, offset, length))); - } - - @Override - public ProtonCompositeBuffer composite() { - checkClosed(); - return ProtonCompositeBuffer.create(this); - } - - @Override - public ProtonCompositeBuffer composite(ProtonBuffer buffer) { - checkClosed(); - return ProtonCompositeBuffer.create(this, buffer); - } - - @Override - public ProtonCompositeBuffer composite(ProtonBuffer[] buffers) { - checkClosed(); - return ProtonCompositeBuffer.create(this, buffers); - } - - private void checkClosed() { - if (closed) { - throw new IllegalStateException("This allocator instance is closed"); - } - } -} - diff --git a/protonj2/src/main/java/org/apache/qpid/protonj2/buffer/netty/Netty5ToProtonBufferAdapter.java b/protonj2/src/main/java/org/apache/qpid/protonj2/buffer/netty/Netty5ToProtonBufferAdapter.java deleted file mode 100644 index 96dd8c99..00000000 --- a/protonj2/src/main/java/org/apache/qpid/protonj2/buffer/netty/Netty5ToProtonBufferAdapter.java +++ /dev/null @@ -1,985 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.qpid.protonj2.buffer.netty; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.nio.channels.ReadableByteChannel; -import java.nio.channels.WritableByteChannel; -import java.nio.charset.Charset; -import java.util.NoSuchElementException; - -import org.apache.qpid.protonj2.buffer.ProtonBuffer; -import org.apache.qpid.protonj2.buffer.ProtonBufferClosedException; -import org.apache.qpid.protonj2.buffer.ProtonBufferComponent; -import org.apache.qpid.protonj2.buffer.ProtonBufferComponentAccessor; -import org.apache.qpid.protonj2.buffer.ProtonBufferIterator; -import org.apache.qpid.protonj2.buffer.ProtonBufferUtils; -import org.apache.qpid.protonj2.resource.SharedResource; - -import io.netty5.buffer.Buffer; -import io.netty5.buffer.BufferAllocator; -import io.netty5.buffer.BufferClosedException; -import io.netty5.buffer.BufferComponent; -import io.netty5.buffer.BufferReadOnlyException; -import io.netty5.buffer.ByteCursor; -import io.netty5.buffer.ComponentIterator; -import io.netty5.buffer.ComponentIterator.Next; - -/** - * Wrapper class for Netty 5 Buffer instances which provides a generic way - * for proton to interact with Netty 4 buffers. - */ -public final class Netty5ToProtonBufferAdapter extends SharedResource - implements ProtonBuffer, ProtonBufferComponentAccessor, ProtonBufferComponent { - - private final Netty5ProtonBufferAllocator allocator; - - private static final Buffer CLOSED_BUFFER; - - static { - CLOSED_BUFFER = BufferAllocator.onHeapUnpooled().allocate(0); - CLOSED_BUFFER.close(); - } - - private Buffer resource; - private BufferComponent resourceComponent; - - /** - * Creates a new {@link Netty5ToProtonBufferAdapter} which wraps the given Netty {@link Buffer}. - * - * @param allocator - * The allocator that created this buffer wrapper - * @param resource - * The {@link Buffer} resource to wrap. - */ - public Netty5ToProtonBufferAdapter(Netty5ProtonBufferAllocator allocator, Buffer resource) { - this.resource = resource; - this.allocator = allocator; - - // To avoid allocation of component iterators we can check that the buffer - // is a single component buffer and it implements the component interface as - // Netty optimizes this case to also avoid allocations. - if (resource.countComponents() == 1 && resource instanceof BufferComponent) { - resourceComponent = (BufferComponent) resource; - } - } - - public Netty5ProtonBufferAllocator allocator() { - return allocator; - } - - /** - * Unwraps the managed Netty {@link Buffer} and releases it from ownership by this - * {@link ProtonBuffer} instance. This effectively closes the {@link ProtonBuffer} - * while also safely handing off the managed Netty resource. - * - * @return the managed Netty {@link Buffer} - */ - public Buffer unwrapAndRelease() { - if (resource != CLOSED_BUFFER) { - try { - return resource; - } finally { - resource = CLOSED_BUFFER; - } - } else { - throw new ProtonBufferClosedException("The buffer has already been closed or transferred"); - } - } - - @Override - public Buffer unwrap() { - ProtonBufferUtils.checkIsClosed(this); - return resource; - } - - @Override - public ProtonBuffer convertToReadOnly() { - resource.makeReadOnly(); - return this; - } - - @Override - public boolean isReadOnly() { - return resource.readOnly(); - } - - @Override - public boolean isComposite() { - return false; - } - - @Override - public int componentCount() { - return resource.countComponents(); - } - - @Override - public int readableComponentCount() { - return resource.countReadableComponents(); - } - - @Override - public int writableComponentCount() { - return resource.countWritableComponents(); - } - - @Override - public boolean isDirect() { - return false; // Buffer components APIs need to allow access to native addresses - } - - @Override - public int implicitGrowthLimit() { - return resource.implicitCapacityLimit(); - } - - @Override - public ProtonBuffer implicitGrowthLimit(int limit) { - resource.implicitCapacityLimit(limit); - return this; - } - - @Override - public ProtonBuffer fill(byte value) { - try { - resource.fill(value); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - return this; - } - - @Override - public int capacity() { - return resource.capacity(); - } - - @Override - public int getReadOffset() { - return resource.readerOffset(); - } - - @Override - public ProtonBuffer setReadOffset(int value) { - resource.readerOffset(value); - return this; - } - - @Override - public int getWriteOffset() { - return resource.writerOffset(); - } - - @Override - public ProtonBuffer setWriteOffset(int value) { - try { - resource.writerOffset(value); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - return this; - } - - @Override - public ProtonBuffer compact() { - try { - resource.compact(); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - return this; - } - - @Override - public void copyInto(int offset, byte[] destination, int destOffset, int length) { - try { - resource.copyInto(offset, destination, destOffset, length); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - } - - @Override - public void copyInto(int offset, ByteBuffer destination, int destOffset, int length) { - try { - resource.copyInto(offset, destination, destOffset, length); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - } - - @Override - public void copyInto(int offset, ProtonBuffer destination, int destOffset, int length) { - try { - if (destination.unwrap() instanceof Buffer) { - resource.copyInto(offset, (Buffer) destination.unwrap(), destOffset, length); - } else { - ProtonBufferUtils.checkIsReadOnly(destination); - ProtonBufferUtils.checkIsClosed(this); - - // Try to reduce bounds-checking by using larger primitives when possible. - for (; length >= Long.BYTES; length -= Long.BYTES, offset += Long.BYTES, destOffset += Long.BYTES) { - destination.setLong(destOffset, getLong(offset)); - } - for (; length >= Integer.BYTES; length -= Integer.BYTES, offset += Integer.BYTES, destOffset += Integer.BYTES) { - destination.setInt(destOffset, getInt(offset)); - } - for (; length > 0; length--, offset++, destOffset++) { - destination.setByte(destOffset, getByte(offset)); - } - } - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - } - - @Override - public ProtonBuffer writeBytes(byte[] source, int offset, int length) { - try { - resource.writeBytes(source, offset, length); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - - return this; - } - - @Override - public ProtonBuffer writeBytes(ByteBuffer source) { - try { - resource.writeBytes(source); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - - return this; - } - - @Override - public ProtonBuffer ensureWritable(int size, int minimumGrowth, boolean allowCompaction) throws IndexOutOfBoundsException, IllegalArgumentException { - try { - resource.ensureWritable(size, minimumGrowth, allowCompaction); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - - return this; - } - - @Override - public ProtonBuffer copy(int index, int length, boolean readOnly) throws IllegalArgumentException { - try { - return allocator.wrap(resource.copy(index, length, readOnly)); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - } - - @Override - public ProtonBuffer split(int splitOffset) { - try { - return allocator.wrap(resource.split(splitOffset)); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - } - - //----- JDK Overrides - - @Override - public String toString(Charset charset) { - return resource.toString(charset); - } - - @Override - public String toString() { - return "Netty5ToProtonBufferAdapter" + - "{ read:" + (resource != null ? resource.readerOffset() : null) + - ", write: " + (resource != null ? resource.writerOffset() : 0) + - ", capacity: " + (resource != null ? resource.capacity() : 0) + "}"; - } - - @Override - public int compareTo(ProtonBuffer buffer) { - return ProtonBufferUtils.compare(this, buffer); - } - - @Override - public boolean equals(Object other) { - if (other instanceof ProtonBuffer) { - return ProtonBufferUtils.equals(this, (ProtonBuffer) other); - } - - return false; - } - - @Override - public int hashCode() { - return ProtonBufferUtils.hashCode(this); - } - - //----- Primitive Get API - - @Override - public byte getByte(int index) { - try { - return resource.getByte(index); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - } - - @Override - public char getChar(int index) { - try { - return resource.getChar(index); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - } - - @Override - public short getShort(int index) { - try { - return resource.getShort(index); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - } - - @Override - public int getInt(int index) { - try { - return resource.getInt(index); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - } - - @Override - public long getLong(int index) { - try { - return resource.getLong(index); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - } - - //----- Primitive Set API - - @Override - public ProtonBuffer setByte(int index, byte value) { - try { - resource.setByte(index, value); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - return this; - } - - @Override - public ProtonBuffer setChar(int index, char value) { - try { - resource.setChar(index, value); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - return this; - } - - @Override - public ProtonBuffer setShort(int index, short value) { - try { - resource.setShort(index, value); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - return this; - } - - @Override - public ProtonBuffer setInt(int index, int value) { - try { - resource.setInt(index, value); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - return this; - } - - @Override - public ProtonBuffer setLong(int index, long value) { - try { - resource.setLong(index, value); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - return this; - } - - //----- Primitive Read API - - @Override - public byte readByte() { - try { - return resource.readByte(); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - } - - @Override - public char readChar() { - try { - return resource.readChar(); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - } - - @Override - public short readShort() { - try { - return resource.readShort(); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - } - - @Override - public int readInt() { - try { - return resource.readInt(); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - } - - @Override - public long readLong() { - try { - return resource.readLong(); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - } - - //----- Primitive Write API - - @Override - public ProtonBuffer writeByte(byte value) { - try { - resource.writeByte(value); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - return this; - } - - @Override - public ProtonBuffer writeChar(char value) { - try { - resource.writeChar(value); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - return this; - } - - @Override - public ProtonBuffer writeShort(short value) { - try { - resource.writeShort(value); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - return this; - } - - @Override - public ProtonBuffer writeInt(int value) { - try { - resource.writeInt(value); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - return this; - } - - @Override - public ProtonBuffer writeLong(long value) { - try { - resource.writeLong(value); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - return this; - } - - //----- IO Handlers - - @Override - public int transferTo(WritableByteChannel channel, int length) throws IOException { - try { - return resource.transferTo(channel, length); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - } - - @Override - public int transferFrom(ReadableByteChannel channel, int length) throws IOException { - try { - return resource.transferFrom(channel, length); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - } - - @Override - public int transferFrom(FileChannel channel, long position, int length) throws IOException { - try { - return resource.transferFrom(channel, position, length); - } catch (RuntimeException e) { - throw translateToProtonException(e); - } - } - - //----- Buff component access - - @Override - public ProtonBufferComponentAccessor componentAccessor() { - if (isClosed()) { - throw ProtonBufferUtils.genericBufferIsClosed(this); - } - - if (resourceComponent != null) { - return (ProtonBufferComponentAccessor) acquire(); - } else { - return new ProtonNetty5BufferComponentAccessor((Netty5ToProtonBufferAdapter) acquire(), resource.forEachComponent()); - } - } - - @Override - public ProtonBufferComponent first() { - return this; - } - - @Override - public ProtonBufferComponent next() { - return null; - } - - //----- Buffer Component API that works only when there is one component in the netty buffer - - @Override - public int getReadableBytes() { - return resourceComponent.readableBytes(); - } - - @Override - public boolean hasReadbleArray() { - return resourceComponent.hasReadableArray(); - } - - @Override - public Netty5ToProtonBufferAdapter advanceReadOffset(int amount) { - return (Netty5ToProtonBufferAdapter) ProtonBuffer.super.advanceReadOffset(amount); - } - - @Override - public byte[] getReadableArray() { - return resourceComponent.readableArray(); - } - - @Override - public int getReadableArrayOffset() { - return resourceComponent.readableArrayOffset(); - } - - @Override - public int getReadableArrayLength() { - return resourceComponent.readableArrayLength(); - } - - @Override - public ByteBuffer getReadableBuffer() { - return resourceComponent.readableBuffer(); - } - - @Override - public int getWritableBytes() { - return resourceComponent.writableBytes(); - } - - @Override - public Netty5ToProtonBufferAdapter advanceWriteOffset(int amount) { - return (Netty5ToProtonBufferAdapter) ProtonBuffer.super.advanceWriteOffset(amount); - } - - @Override - public boolean hasWritableArray() { - return resourceComponent.hasWritableArray(); - } - - @Override - public byte[] getWritableArray() { - return resourceComponent.writableArray(); - } - - @Override - public int getWritableArrayOffset() { - return resourceComponent.writableArrayOffset(); - } - - @Override - public int getWritableArrayLength() { - return resourceComponent.writableArrayLength(); - } - - @Override - public ByteBuffer getWritableBuffer() { - return resourceComponent.writableBuffer(); - } - - @Override - public long getNativeAddress() { - return resourceComponent.baseNativeAddress(); - } - - @Override - public long getNativeReadAddress() { - return resourceComponent.readableNativeAddress(); - } - - @Override - public long getNativeWriteAddress() { - return resourceComponent.writableNativeAddress(); - } - - //----- Buffer Iteration API - - @Override - public ProtonBufferIterator bufferIterator() { - return bufferIterator(getReadOffset(), getReadableBytes()); - } - - @Override - public ProtonBufferIterator bufferIterator(int offset, int length) { - ProtonBufferUtils.checkIsClosed(this); - ProtonBufferUtils.checkIsNotNegative(length, "length"); - ProtonBufferUtils.checkIsNotNegative(offset, "length"); - - if (offset + length > resource.capacity()) { - throw new IndexOutOfBoundsException( - "The iterator cannot read beyond the bounds of the buffer: offset=" + offset + ", length=" + length); - } - - return new Netty5ToProtonBufferReverseIterator(resource.openCursor(offset, length)); - } - - @Override - public ProtonBufferIterator bufferReverseIterator(int offset, int length) { - ProtonBufferUtils.checkIsClosed(this); - ProtonBufferUtils.checkIsNotNegative(length, "length"); - ProtonBufferUtils.checkIsNotNegative(offset, "length"); - - if (offset >= capacity()) { - throw new IndexOutOfBoundsException( - "Read offset must be within the bounds of the buffer: offset = " + offset + ", capacity = " + capacity()); - } - - if (offset - length < -1) { - throw new IndexOutOfBoundsException( - "Cannot read past start of buffer: offset = " + offset + ", length = " + length); - } - - return new Netty5ToProtonBufferReverseIterator(resource.openReverseCursor(offset, length)); - } - - private static final class Netty5ToProtonBufferReverseIterator implements ProtonBufferIterator { - - private final ByteCursor cursor; - - public Netty5ToProtonBufferReverseIterator(ByteCursor cursor) { - this.cursor = cursor; - } - - @Override - public boolean hasNext() { - return cursor.bytesLeft() > 0; - } - - @Override - public byte next() { - if (!cursor.readByte()) { - throw new NoSuchElementException("Cannot read outside the iterator bounds"); - } - - return cursor.getByte(); - } - - @Override - public int remaining() { - return cursor.bytesLeft(); - } - - @Override - public int offset() { - return cursor.currentOffset(); - } - } - - //----- Buffer search API - - @Override - public int indexOf(byte needle, int offset, int length) { - ProtonBufferUtils.checkIsClosed(this); - - if (offset < getReadOffset() || getWriteOffset() < offset + length) { - throw new IndexOutOfBoundsException("Cannot search past the readable bounds of this buffer"); - } - - final int count = resource.bytesBefore(needle); - if (count < 0) { - return count; - } - - final int indexOf = offset + count; - - if (offset > indexOf || offset + length < indexOf) { - return -1; - } - - return indexOf; - } - - //----- Shared resource API - - @Override - protected void releaseResourceOwnership() { - if (resource != null && resource.isAccessible()) { - resource.close(); - resource = CLOSED_BUFFER; - } - } - - @Override - protected ProtonBuffer transferTheResource() { - final Buffer transferred = resource; - resource = CLOSED_BUFFER; - - return new Netty5ToProtonBufferAdapter(allocator, transferred); - } - - @Override - protected RuntimeException resourceIsClosedException() { - return ProtonBufferUtils.genericBufferIsClosed(this); - } - - //----- Support API for the buffer wrapper - - private RuntimeException translateToProtonException(RuntimeException e) { - RuntimeException result = e; - - if (e instanceof BufferReadOnlyException) { - result = ProtonBufferUtils.genericBufferIsReadOnly(this); - result.addSuppressed(e); - } else if (e instanceof BufferClosedException) { - result = ProtonBufferUtils.genericBufferIsClosed(this); - result.addSuppressed(e); - } - - return result; - } - - @SuppressWarnings("rawtypes") - private static final class ProtonNetty5BufferComponentAccessor implements ProtonBufferComponentAccessor, ProtonBufferComponent { - - private final Netty5ToProtonBufferAdapter adapter; - - private final ComponentIterator resourceIterator; - - private Next current; - private BufferComponent currentComponent; - - public ProtonNetty5BufferComponentAccessor(Netty5ToProtonBufferAdapter adapter, ComponentIterator iterator) { - this.adapter = adapter; - this.resourceIterator = iterator; - } - - @Override - public void close() { - current = null; - currentComponent = null; - - resourceIterator.close(); - adapter.close(); - } - - @Override - public ProtonBufferComponent first() { - current = resourceIterator.first(); - currentComponent = (BufferComponent) current; - - return this; - } - - @Override - public ProtonBufferComponent next() { - if (current != null) { - current = current.next(); - currentComponent = (BufferComponent) current; - } - - return current != null ? this : null; - } - - @Override - public Object unwrap() { - return adapter.resource; - } - - @Override - public int getReadableBytes() { - return currentComponent.readableBytes(); - } - - @Override - public boolean hasReadbleArray() { - return currentComponent.hasReadableArray(); - } - - @Override - public ProtonBufferComponent advanceReadOffset(int amount) { - currentComponent.skipReadableBytes(amount); - return this; - } - - @Override - public byte[] getReadableArray() { - return currentComponent.readableArray(); - } - - @Override - public int getReadableArrayOffset() { - return currentComponent.readableArrayOffset(); - } - - @Override - public int getReadableArrayLength() { - return currentComponent.readableArrayLength(); - } - - @Override - public ByteBuffer getReadableBuffer() { - return currentComponent.readableBuffer(); - } - - @Override - public int getWritableBytes() { - return currentComponent.writableBytes(); - } - - @Override - public ProtonBufferComponent advanceWriteOffset(int amount) { - currentComponent.skipWritableBytes(amount); - return this; - } - - @Override - public boolean hasWritableArray() { - return currentComponent.hasWritableArray(); - } - - @Override - public byte[] getWritableArray() { - return currentComponent.writableArray(); - } - - @Override - public int getWritableArrayOffset() { - return currentComponent.writableArrayOffset(); - } - - @Override - public int getWritableArrayLength() { - return currentComponent.writableArrayLength(); - } - - @Override - public ByteBuffer getWritableBuffer() { - return currentComponent.writableBuffer(); - } - - @Override - public long getNativeAddress() { - return currentComponent.baseNativeAddress(); - } - - @Override - public long getNativeReadAddress() { - return currentComponent.readableNativeAddress(); - } - - @Override - public long getNativeWriteAddress() { - return currentComponent.writableNativeAddress(); - } - - @Override - public ProtonBufferIterator bufferIterator() { - return new Netty5ToProtonBufferIterator(currentComponent.openCursor()); - } - - private final class Netty5ToProtonBufferIterator implements ProtonBufferIterator { - - private final ByteCursor cursor; - - public Netty5ToProtonBufferIterator(ByteCursor cursor) { - this.cursor = cursor; - } - - @Override - public boolean hasNext() { - return cursor.bytesLeft() > 0; - } - - @Override - public byte next() { - if (cursor.readByte()) { - throw new NoSuchElementException("Cannot read outside the iterator bounds"); - } - - return cursor.getByte(); - } - - @Override - public int remaining() { - return cursor.bytesLeft(); - } - - @Override - public int offset() { - return cursor.currentOffset(); - } - } - } -} diff --git a/protonj2/src/main/java/org/apache/qpid/protonj2/buffer/netty/ProtonBufferToNetty5Adapter.java b/protonj2/src/main/java/org/apache/qpid/protonj2/buffer/netty/ProtonBufferToNetty5Adapter.java deleted file mode 100644 index c35cead4..00000000 --- a/protonj2/src/main/java/org/apache/qpid/protonj2/buffer/netty/ProtonBufferToNetty5Adapter.java +++ /dev/null @@ -1,1109 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.qpid.protonj2.buffer.netty; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.nio.channels.ReadableByteChannel; -import java.nio.channels.WritableByteChannel; -import java.nio.charset.Charset; - -import org.apache.qpid.protonj2.buffer.ProtonBuffer; -import org.apache.qpid.protonj2.buffer.ProtonBufferClosedException; -import org.apache.qpid.protonj2.buffer.ProtonBufferComponent; -import org.apache.qpid.protonj2.buffer.ProtonBufferComponentAccessor; -import org.apache.qpid.protonj2.buffer.ProtonBufferIterator; -import org.apache.qpid.protonj2.buffer.ProtonBufferReadOnlyException; - -import io.netty5.buffer.Buffer; -import io.netty5.buffer.BufferClosedException; -import io.netty5.buffer.BufferComponent; -import io.netty5.buffer.BufferReadOnlyException; -import io.netty5.buffer.ByteCursor; -import io.netty5.buffer.ComponentIterator; -import io.netty5.buffer.ComponentIterator.Next; -import io.netty5.buffer.internal.InternalBufferUtils; -import io.netty5.util.Send; - -/** - * Adapts a {@link ProtonBuffer} instance to a Netty 5 {@link Buffer} - */ -public class ProtonBufferToNetty5Adapter implements Buffer { - - private ProtonBuffer resource; - - public ProtonBufferToNetty5Adapter(ProtonBuffer resource) { - this.resource = resource; - } - - @Override - public void close() { - resource.close(); - } - - @Override - public Send send() { - try { - final ProtonBuffer transferred = resource.transfer(); - - return new Send() { - - @Override - public Buffer receive() { - return new ProtonBufferToNetty5Adapter(transferred); - } - - @Override - public void close() { - transferred.close(); - } - - @Override - public boolean referentIsInstanceOf(Class cls) { - return cls.isAssignableFrom(ProtonBufferToNetty5Adapter.class); - } - }; - } catch (RuntimeException e) { - throw translateToNettyException(e); - } - } - - @Override - public boolean isAccessible() { - return !resource.isClosed(); - } - - @Override - public Buffer compact() { - try { - resource.compact(); - return this; - } catch (RuntimeException e) { - throw translateToNettyException(e); - } - } - - @Override - public Buffer makeReadOnly() { - try { - resource.convertToReadOnly(); - return this; - } catch (RuntimeException e) { - throw translateToNettyException(e); - } - } - - @Override - public boolean readOnly() { - return resource.isReadOnly(); - } - - @Override - public Buffer fill(byte value) { - try { - resource.fill(value); - return this; - } catch (RuntimeException e) { - throw translateToNettyException(e); - } - } - - @Override - public boolean isDirect() { - return false; // NOTE: resource.isDirect(); is only reasonable if we also expose the native address - } - - @Override - public int capacity() { - return resource.capacity(); - } - - @Override - public Buffer implicitCapacityLimit(int limit) { - try { - resource.implicitGrowthLimit(limit); - return this; - } catch (RuntimeException e) { - throw translateToNettyException(e); - } - } - - @Override - public int implicitCapacityLimit() { - return resource.implicitGrowthLimit(); - } - - @Override - public int readerOffset() { - return resource.getReadOffset(); - } - - @Override - public Buffer readerOffset(int offset) { - try { - resource.setReadOffset(offset); - return this; - } catch (RuntimeException e) { - throw translateToNettyException(e); - } - } - - @Override - public int writerOffset() { - return resource.getWriteOffset(); - } - - @Override - public Buffer writerOffset(int offset) { - try { - resource.setWriteOffset(offset); - return this; - } catch (RuntimeException e) { - throw translateToNettyException(e); - } - } - - @Override - public Buffer ensureWritable(int size, int minimumGrowth, boolean allowCompaction) { - try { - resource.ensureWritable(size, minimumGrowth, allowCompaction); - return this; - } catch (RuntimeException e) { - throw translateToNettyException(e); - } - } - - @Override - public void copyInto(int srcPos, byte[] dest, int destPos, int length) { - try { - resource.copyInto(srcPos, dest, destPos, length); - } catch (RuntimeException e) { - throw translateToNettyException(e); - } - } - - @Override - public void copyInto(int srcPos, ByteBuffer dest, int destPos, int length) { - try { - resource.copyInto(srcPos, dest, destPos, length); - } catch (RuntimeException e) { - throw translateToNettyException(e); - } - } - - @Override - public void copyInto(int srcPos, Buffer destination, int destPos, int length) { - if (!destination.isAccessible()) { - throw new BufferClosedException("Destination buffer is closed"); - } - if (destination.readOnly()) { - throw new BufferReadOnlyException("Destination buffer is read only"); - } - - checkCopyIntoArgs(srcPos, length, destPos, destination.capacity()); - - try { - // Try to reduce bounds-checking by using larger primitives when possible. - for (; length >= Long.BYTES; length -= Long.BYTES, srcPos += Long.BYTES, destPos += Long.BYTES) { - destination.setLong(destPos, getLong(srcPos)); - } - for (; length >= Integer.BYTES; length -= Integer.BYTES, srcPos += Integer.BYTES, destPos += Integer.BYTES) { - destination.setInt(destPos, getInt(srcPos)); - } - for (; length > 0; length--, srcPos++, destPos++) { - destination.setByte(destPos, getByte(srcPos)); - } - } catch (RuntimeException e) { - throw translateToNettyException(e); - } - } - - @Override - public Buffer copy(int offset, int length, boolean readOnly) { - try { - return new ProtonBufferToNetty5Adapter(resource.copy(offset, length)); - } catch (RuntimeException e) { - throw translateToNettyException(e); - } - } - - @Override - public Buffer split(int splitOffset) { - try { - return new ProtonBufferToNetty5Adapter(resource.split(splitOffset)); - } catch (RuntimeException e) { - throw translateToNettyException(e); - } - } - - @Override - public int bytesBefore(byte needle) { - try { - final int indexOf = resource.indexOf(needle); - if (indexOf >= 0) { - return indexOf - resource.getReadOffset(); - } - - return -1; - } catch (RuntimeException e) { - throw translateToNettyException(e); - } - } - - @Override - public int bytesBefore(Buffer needle) { - return InternalBufferUtils.bytesBefore(this, null, needle, null); - } - - //----- Buffer input and output APIs - - @Override - public int transferTo(WritableByteChannel channel, int length) throws IOException { - try { - return resource.transferTo(channel, length); - } catch (RuntimeException e) { - throw translateToNettyException(e); - } - } - - @Override - public int transferFrom(FileChannel channel, long position, int length) throws IOException { - try { - return resource.transferFrom(channel, position, length); - } catch (RuntimeException e) { - throw translateToNettyException(e); - } - } - - @Override - public int transferFrom(ReadableByteChannel channel, int length) throws IOException { - try { - return resource.transferFrom(channel, length); - } catch (RuntimeException e) { - throw translateToNettyException(e); - } - } - - //----- Buffer cursors API - - @Override - public ByteCursor openCursor() { - return openCursor(readerOffset(), readableBytes()); - } - - @Override - public ByteCursor openCursor(int fromOffset, int length) { - try { - return new ProtonBufferToNetty5ByteCursor(resource.bufferIterator(fromOffset, length)); - } catch (RuntimeException e) { - throw translateToNettyException(e); - } - } - - @Override - public ByteCursor openReverseCursor(int fromOffset, int length) { - try { - return new ProtonBufferToNetty5ByteCursor(resource.bufferReverseIterator(fromOffset, length)); - } catch (RuntimeException e) { - throw translateToNettyException(e); - } - } - - private final class ProtonBufferToNetty5ByteCursor implements ByteCursor { - - private final ProtonBufferIterator iterator; - - private byte lastReadByte = -1; - - public ProtonBufferToNetty5ByteCursor(ProtonBufferIterator iterator) { - this.iterator = iterator; - } - - @Override - public boolean readByte() { - if (iterator.hasNext()) { - lastReadByte = iterator.next(); - return true; - } - - return false; - } - - @Override - public byte getByte() { - return lastReadByte; - } - - @Override - public int currentOffset() { - return iterator.offset(); - } - - @Override - public int bytesLeft() { - return iterator.remaining(); - } - } - - //----- Buffer components API - - @Override - public int countComponents() { - return resource.componentCount(); - } - - @Override - public int countReadableComponents() { - return resource.readableComponentCount(); - } - - @Override - public int countWritableComponents() { - return resource.writableComponentCount(); - } - - @Override - public ComponentIterator forEachComponent() { - try { - return new ProtonBufferComponentIterator<>(resource.componentAccessor()); - } catch (RuntimeException e) { - throw translateToNettyException(e); - } - } - - //----- Primitive indexed set API - - @Override - public Buffer setByte(int woff, byte value) { - try { - resource.setByte(woff, value); - return this; - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public Buffer setUnsignedByte(int woff, int value) { - try { - resource.setByte(woff, (byte)(value & 0xFF)); - return this; - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public Buffer setChar(int woff, char value) { - try { - resource.setChar(woff, value); - return this; - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public Buffer setShort(int woff, short value) { - try { - resource.setShort(woff, value); - return this; - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public Buffer setUnsignedShort(int woff, int value) { - try { - resource.setUnsignedShort(woff, (short)(value & 0x0000FFFF)); - return null; - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public Buffer setMedium(int woff, int value) { - try { - resource.setByte(woff, (byte) (value >>> 16)); - resource.setByte(woff + 1, (byte) (value >>> 8)); - resource.setByte(woff + 2, (byte) (value >>> 0)); - - return this; - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public Buffer setUnsignedMedium(int woff, int value) { - try { - resource.setByte(woff, (byte) (value >>> 16)); - resource.setByte(woff + 1, (byte) (value >>> 8)); - resource.setByte(woff + 2, (byte) (value >>> 0)); - - return this; - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public Buffer setInt(int woff, int value) { - try { - resource.setInt(woff, value); - return this; - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public Buffer setUnsignedInt(int woff, long value) { - try { - resource.setUnsignedInt(woff, (int)(value & 0x00000000FFFFFFFFl)); - return this; - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public Buffer setLong(int woff, long value) { - try { - resource.setLong(woff, value); - return this; - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public Buffer setFloat(int woff, float value) { - try { - resource.setFloat(woff, value); - return this; - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public Buffer setDouble(int woff, double value) { - try { - resource.setDouble(woff, value); - return this; - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - //----- Primitive relative write API - - @Override - public Buffer writeByte(byte value) { - try { - resource.writeByte(value); - return this; - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public Buffer writeUnsignedByte(int value) { - try { - resource.writeByte((byte) (value & 0xFF)); - return this; - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public Buffer writeShort(short value) { - try { - resource.writeShort(value); - return this; - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public Buffer writeUnsignedShort(int value) { - try { - resource.writeShort((short) (value & 0x00FF)); - return this; - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public Buffer writeChar(char value) { - try { - resource.writeChar(value); - return this; - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public Buffer writeMedium(int value) { - try { - resource.writeByte((byte) (value >>> 16)); - resource.writeByte((byte) (value >>> 8)); - resource.writeByte((byte) (value >>> 0)); - - return this; - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public Buffer writeUnsignedMedium(int value) { - try { - resource.writeByte((byte) (value >>> 16)); - resource.writeByte((byte) (value >>> 8)); - resource.writeByte((byte) (value >>> 0)); - - return this; - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public Buffer writeInt(int value) { - try { - resource.writeInt(value); - return this; - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public Buffer writeUnsignedInt(long value) { - try { - resource.writeInt((int)(value & 0x00000000FFFFFFFFl)); - return this; - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public Buffer writeLong(long value) { - try { - resource.writeLong(value); - return this; - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public Buffer writeFloat(float value) { - try { - resource.writeFloat(value); - return this; - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public Buffer writeDouble(double value) { - try { - resource.writeDouble(value); - return this; - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public Buffer writeCharSequence(CharSequence source, Charset charset) { - try { - resource.writeCharSequence(source, charset); - return this; - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - //----- Primitive indexed get API - - @Override - public byte getByte(int index) { - try { - return resource.getByte(index); - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public char getChar(int index) { - try { - return resource.getChar(index); - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public int getUnsignedByte(int index) { - try { - return resource.getUnsignedByte(index); - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public int getUnsignedShort(int index) { - try { - return resource.getUnsignedShort(index); - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public short getShort(int index) { - try { - return resource.getShort(index); - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public int getMedium(int index) { - try { - return (getByte(index)) << 16 | - (getByte(index + 1) & 0xFF) << 8 | - (getByte(index + 2) & 0xFF) << 0; - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public int getUnsignedMedium(int index) { - try { - return ((getByte(index)) << 16 | - (getByte(index + 1) & 0xFF) << 8 | - (getByte(index + 2) & 0xFF) << 0) & 0xFFFFFF; - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public int getInt(int index) { - try { - return resource.getInt(index); - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public long getUnsignedInt(int index) { - try { - return resource.getUnsignedInt(index); - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public long getLong(int index) { - try { - return resource.getLong(index); - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public float getFloat(int index) { - try { - return resource.getFloat(index); - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public double getDouble(int index) { - try { - return resource.getDouble(index); - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - //----- Primitive relative read API - - @Override - public byte readByte() { - try { - return resource.readByte(); - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public int readUnsignedByte() { - try { - return resource.readUnsignedByte(); - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public char readChar() { - try { - return resource.readChar(); - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public short readShort() { - try { - return resource.readShort(); - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public int readUnsignedShort() { - try { - return readShort() & 0x0000FFFF; - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public int readMedium() { - try { - return readByte() << 16 | - (readByte() & 0xFF) << 8 | - (readByte() & 0xFF) << 0; - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public int readUnsignedMedium() { - try { - return ((readByte()) << 16 | - (readByte() & 0xFF) << 8 | - (readByte() & 0xFF) << 0) & 0xFFFFFF; - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public int readInt() { - try { - return resource.readInt(); - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public long readUnsignedInt() { - try { - return readInt() & 0x00000000FFFFFFFFl; - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public long readLong() { - try { - return resource.readLong(); - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public float readFloat() { - try { - return resource.readFloat(); - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public double readDouble() { - try { - return resource.readDouble(); - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - @Override - public CharSequence readCharSequence(int length, Charset charset) { - try { - return resource.readCharSequence(length, charset); - } catch (RuntimeException ex) { - throw translateToNettyException(ex); - } - } - - //----- Object API overrides - - @Override - public boolean equals(Object o) { - return o instanceof Buffer && InternalBufferUtils.equals(this, (Buffer) o); - } - - @Override - public int hashCode() { - return InternalBufferUtils.hashCode(this); - } - - //----- Internal API for validation of arguments - - private void checkCopyIntoArgs(int srcPos, int length, int destPos, int destLength) { - if (resource.isClosed()) { - throw new BufferClosedException("The wrapped ProtonBuffer is closed"); - } - if (srcPos < 0) { - throw new IndexOutOfBoundsException("The srcPos cannot be negative: " + srcPos + '.'); - } - if (length < 0) { - throw new IndexOutOfBoundsException("The length value cannot be negative " + length + "."); - } - if (resource.capacity() < srcPos + length) { - throw new IndexOutOfBoundsException("The srcPos + length is beyond the end of the buffer: " + - "srcPos = " + srcPos + ", length = " + length + '.'); - } - if (destPos < 0) { - throw new IndexOutOfBoundsException("The destPos cannot be negative: " + destPos + '.'); - } - if (destLength < destPos + length) { - throw new IndexOutOfBoundsException("The destPos + length is beyond the end of the destination: " + - "destPos = " + destPos + ", length = " + length + '.'); - } - } - - private RuntimeException translateToNettyException(RuntimeException e) { - RuntimeException result = e; - - if (e instanceof ProtonBufferReadOnlyException) { - result = new BufferReadOnlyException("Buffer is read-only"); - result.addSuppressed(e); - } else if (e instanceof ProtonBufferClosedException) { - result = new BufferClosedException("Buffer is closed"); - result.addSuppressed(e); - } - - return result; - } - - //----- Wrappers needed to adapt to the Netty 5 BufferCompoent and ComponentIterator API - - private static class ProtonBufferComponentIterator implements Next, BufferComponent, ComponentIterator { - - private ProtonBufferComponentAccessor accessor; - - private ProtonBufferComponent current; - - public ProtonBufferComponentIterator(ProtonBufferComponentAccessor accessor) { - this.accessor = accessor; - } - - @Override - public void close() { - current = null; - - accessor.close(); - } - - @SuppressWarnings("unchecked") - @Override - public T first() { - return (current = accessor.first()) != null ? (T) this : null; - } - - @SuppressWarnings("unchecked") - @Override - public N next() { - if (current != null) { - current = accessor.next(); - } - - return current != null ? (N) this : null; - } - - @Override - public boolean hasReadableArray() { - return current.hasReadbleArray(); - } - - @Override - public boolean hasWritableArray() { - return current.hasWritableArray(); - } - - @Override - public byte[] readableArray() { - return current.getReadableArray(); - } - - @Override - public byte[] writableArray() { - return current.getWritableArray(); - } - - @Override - public int readableArrayOffset() { - return current.getReadableArrayOffset(); - } - - @Override - public int writableArrayOffset() { - return current.getWritableArrayOffset(); - } - - @Override - public int readableArrayLength() { - return current.getReadableArrayLength(); - } - - @Override - public int writableArrayLength() { - return current.getWritableArrayLength(); - } - - @Override - public long baseNativeAddress() { - return current.getNativeAddress(); - } - - @Override - public long readableNativeAddress() { - return current.getNativeReadAddress(); - } - - @Override - public long writableNativeAddress() { - return current.getNativeWriteAddress(); - } - - @Override - public ByteBuffer readableBuffer() { - return current.getReadableBuffer(); - } - - @Override - public ByteBuffer writableBuffer() { - return current.getWritableBuffer(); - } - - @Override - public int readableBytes() { - return current.getReadableBytes(); - } - - @Override - public int writableBytes() { - return current.getWritableBytes(); - } - - @Override - public ByteCursor openCursor() { - return new ProtonByteCursorAdapter(current.bufferIterator()); - } - - @Override - public BufferComponent skipReadableBytes(int byteCount) { - current.advanceReadOffset(byteCount); - return this; - } - - @Override - public BufferComponent skipWritableBytes(int byteCount) { - current.advanceWriteOffset(byteCount); - return this; - } - - - } - - private final static class ProtonByteCursorAdapter implements ByteCursor { - - private final ProtonBufferIterator iterator; - - private byte lastByte = -1; - - public ProtonByteCursorAdapter(ProtonBufferIterator iterator) { - this.iterator = iterator; - } - - @Override - public boolean readByte() { - if (iterator.hasNext()) { - lastByte = iterator.next(); - return true; - } - - return false; - } - - @Override - public byte getByte() { - return lastByte; - } - - @Override - public int currentOffset() { - return iterator.offset(); - } - - @Override - public int bytesLeft() { - return iterator.remaining(); - } - } -} diff --git a/protonj2/src/test/java/org/apache/qpid/protonj2/buffer/ProtonAbstractBufferTest.java b/protonj2/src/test/java/org/apache/qpid/protonj2/buffer/ProtonAbstractBufferTest.java index c390123e..fc1c7f9b 100644 --- a/protonj2/src/test/java/org/apache/qpid/protonj2/buffer/ProtonAbstractBufferTest.java +++ b/protonj2/src/test/java/org/apache/qpid/protonj2/buffer/ProtonAbstractBufferTest.java @@ -475,7 +475,6 @@ public void testWriteBytesHeapByteBufferMustExpandCapacityIfBufferIsTooSmall() { } } - @SuppressWarnings("resource") @Test public void testWriteBytesHeapByteBufferMustThrowIfCannotBeExpanded() { // With zero offsets @@ -530,7 +529,6 @@ public void testWiteBytesDirectByteBufferMustExpandCapacityIfBufferIsTooSmall() } } - @SuppressWarnings("resource") @Test public void testWriteBytesDirectByteBufferMustThrowIfCannotBeExpanded() { // With zero offsets @@ -583,7 +581,6 @@ public void testWriteBytesByteArrayMustExpandCapacityIfTooSmall() { } } - @SuppressWarnings("resource") @Test public void testWriteBytesByteArrayMustThrowIfCannotBeExpanded() { // Starting at offsets zero. @@ -634,7 +631,6 @@ public void testWriteBytesByteArrayWithOffsetMustExpandCapacityIfTooSmall() { } } - @SuppressWarnings("resource") @Test public void testWriteBytesByteArrayWithOffsetMustThrowIfCannotBeExpanded() { // Starting at offsets zero. @@ -687,7 +683,6 @@ public void testWriteBytesBufferMustExpandCapacityIfTooSmall() { } } - @SuppressWarnings("resource") @Test public void testWriteBytesBufferMustThrowIfCannotBeExpanded() { // Starting at offsets zero. @@ -2068,7 +2063,6 @@ public void testSplitDoesNotReduceImplicitCapacityLimit() { } } - @SuppressWarnings("resource") @Test public void testEnsureWritableCanGrowBeyondImplicitCapacityLimit() { try (ProtonBufferAllocator allocator = createTestCaseAllocator(); ProtonBuffer buf = allocator.allocate(8).implicitGrowthLimit(8)) { @@ -2084,7 +2078,6 @@ public void testEnsureWritableCanGrowBeyondImplicitCapacityLimit() { } } - @SuppressWarnings("resource") @Test public void testWritesMustThrowIfSizeWouldGoBeyondImplicitCapacityLimit() { try (ProtonBufferAllocator allocator = createTestCaseAllocator()) { @@ -2545,7 +2538,6 @@ public void testSplitOfReadOnlyBufferMustBeReadOnly() { } } - @SuppressWarnings("resource") @Test public void testAllocatingOnClosedAllocatorMustThrow() { ProtonBufferAllocator allocator = createTestCaseAllocator(); @@ -3018,7 +3010,6 @@ public void testCopyIntoByteBufferOnEmptyBufferFromFullyReadBuffer() { } } - @SuppressWarnings("resource") @Test public void testReadOnlyBuffersCannotChangeWriteOffset() { try (ProtonBufferAllocator allocator = createTestCaseAllocator(); @@ -4934,7 +4925,6 @@ public void testCompositeBufferComponentCountsUpdateWithChangeAfterFlattening() } } - @SuppressWarnings("resource") @Test public void testIteratingComponentOnClosedBufferMustThrow() { try (ProtonBufferAllocator allocator = createTestCaseAllocator()) { @@ -5558,7 +5548,6 @@ public void testIndexOfByteOnEmptyBufferReturnsNoResult() { } } - @SuppressWarnings("resource") protected static void verifyInaccessible(ProtonBuffer buf) { verifyReadInaccessible(buf); diff --git a/protonj2/src/test/java/org/apache/qpid/protonj2/buffer/impl/ProtonCompositeBufferImplTest.java b/protonj2/src/test/java/org/apache/qpid/protonj2/buffer/impl/ProtonCompositeBufferImplTest.java index ad113835..024226fe 100644 --- a/protonj2/src/test/java/org/apache/qpid/protonj2/buffer/impl/ProtonCompositeBufferImplTest.java +++ b/protonj2/src/test/java/org/apache/qpid/protonj2/buffer/impl/ProtonCompositeBufferImplTest.java @@ -39,11 +39,11 @@ import org.apache.qpid.protonj2.buffer.ProtonBufferComponentAccessor; import org.apache.qpid.protonj2.buffer.ProtonBufferReadOnlyException; import org.apache.qpid.protonj2.buffer.ProtonCompositeBuffer; -import org.apache.qpid.protonj2.buffer.netty.Netty5ProtonBufferAllocator; +import org.apache.qpid.protonj2.buffer.netty.Netty4ProtonBufferAllocator; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -import io.netty5.buffer.BufferAllocator; +import io.netty.buffer.PooledByteBufAllocator; /** * Tests for the proton composite buffer implementation. @@ -598,7 +598,7 @@ public void testAppendingToNonOwnedCompositeBufferMustThrow() { } @SuppressWarnings("resource") - @Test + @Test public void testAppendingCompositeBufferToItselfMustThrow() { try (ProtonBufferAllocator allocator = createProtonDefaultAllocator()) { ProtonCompositeBuffer composite; @@ -1932,7 +1932,7 @@ public void testSplitCompositeOfManyBuffersInsideOFMiddleSegment() { @Test public void testBufferExposesNativeAddressValues() { - try (ProtonBufferAllocator allocator = new Netty5ProtonBufferAllocator(BufferAllocator.offHeapUnpooled()); + try (ProtonBufferAllocator allocator = new Netty4ProtonBufferAllocator(PooledByteBufAllocator.DEFAULT); ProtonBuffer buffer = allocator.allocate(16)) { buffer.writeLong(Long.MAX_VALUE); @@ -1950,7 +1950,7 @@ public void testBufferExposesNativeAddressValues() { @Test public void testBufferExposesNativeAddressValuesForNativeBackedBuffers() { - try (ProtonBufferAllocator offHeapAllocator = new Netty5ProtonBufferAllocator(BufferAllocator.offHeapUnpooled()); + try (ProtonBufferAllocator offHeapAllocator = new Netty4ProtonBufferAllocator(PooledByteBufAllocator.DEFAULT); ProtonBufferAllocator onHeapAllocator = createProtonDefaultAllocator(); ProtonCompositeBuffer buffer = onHeapAllocator.composite()) { diff --git a/protonj2/src/test/java/org/apache/qpid/protonj2/buffer/netty/Netty5ProtonBufferAdapterTest.java b/protonj2/src/test/java/org/apache/qpid/protonj2/buffer/netty/Netty5ProtonBufferAdapterTest.java deleted file mode 100644 index 3d94ce1a..00000000 --- a/protonj2/src/test/java/org/apache/qpid/protonj2/buffer/netty/Netty5ProtonBufferAdapterTest.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.qpid.protonj2.buffer.netty; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.apache.qpid.protonj2.buffer.ProtonBuffer; -import org.apache.qpid.protonj2.buffer.ProtonBufferAllocator; -import org.apache.qpid.protonj2.buffer.ProtonBufferComponent; -import org.apache.qpid.protonj2.buffer.ProtonBufferComponentAccessor; -import org.junit.jupiter.api.Test; - -import io.netty5.buffer.Buffer; -import io.netty5.buffer.BufferAllocator; - -/** - * Test the Netty 5 to {@link ProtonBuffer} adapter - */ -public class Netty5ProtonBufferAdapterTest extends NettyBufferAdapterTestBase { - - @Override - public ProtonBufferAllocator createTestCaseAllocator() { - return new Netty5ProtonBufferAllocator(BufferAllocator.onHeapUnpooled()); - } - - @Test - public void testBufferExposesNativeAddressValues() { - try (ProtonBufferAllocator netty = new Netty5ProtonBufferAllocator(BufferAllocator.offHeapUnpooled()); - ProtonBuffer nettyBuffer = netty.allocate(16)) { - - nettyBuffer.writeLong(Long.MAX_VALUE); - nettyBuffer.readByte(); - - try (ProtonBufferComponentAccessor accessor = nettyBuffer.componentAccessor()) { - for (ProtonBufferComponent component : accessor.components()) { - assertTrue(component.getNativeAddress() != 0); - assertTrue(component.getNativeReadAddress() != 0); - assertTrue(component.getNativeWriteAddress() != 0); - } - } - } - } - - @Test - public void testBufferCloseReleasesBuffer() { - try (ProtonBufferAllocator netty = createTestCaseAllocator(); - ProtonBuffer nettyBuffer = netty.allocate(16)) { - - Buffer buffer = (Buffer) nettyBuffer.unwrap(); - - assertTrue(buffer.isAccessible()); - nettyBuffer.close(); - assertFalse(buffer.isAccessible()); - } - } -} diff --git a/protonj2/src/test/java/org/apache/qpid/protonj2/buffer/netty/ProtonBufferToNetty5AdapterTest.java b/protonj2/src/test/java/org/apache/qpid/protonj2/buffer/netty/ProtonBufferToNetty5AdapterTest.java deleted file mode 100644 index 4a957bea..00000000 --- a/protonj2/src/test/java/org/apache/qpid/protonj2/buffer/netty/ProtonBufferToNetty5AdapterTest.java +++ /dev/null @@ -1,1596 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 org.apache.qpid.protonj2.buffer.netty; - -import static java.nio.ByteOrder.BIG_ENDIAN; -import static java.nio.file.StandardOpenOption.DELETE_ON_CLOSE; -import static java.nio.file.StandardOpenOption.READ; -import static java.nio.file.StandardOpenOption.WRITE; -import static org.junit.jupiter.api.Assertions.assertArrayEquals; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.ReadOnlyBufferException; -import java.nio.channels.ClosedChannelException; -import java.nio.channels.FileChannel; -import java.nio.channels.ReadableByteChannel; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.ThreadLocalRandom; -import java.util.concurrent.atomic.AtomicInteger; - -import org.apache.qpid.protonj2.buffer.ProtonBuffer; -import org.apache.qpid.protonj2.buffer.ProtonBufferAllocator; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; -import org.junit.jupiter.api.io.TempDir; - -import io.netty5.buffer.Buffer; -import io.netty5.buffer.BufferAllocator; -import io.netty5.buffer.BufferClosedException; -import io.netty5.buffer.BufferReadOnlyException; -import io.netty5.buffer.ByteCursor; -import io.netty5.buffer.CompositeBuffer; -import io.netty5.buffer.internal.InternalBufferUtils; -import io.netty5.util.AsciiString; - -/** - * Test the wrapper that adapts any {@link ProtonBuffer} to a Netty 5 {@link Buffer} - */ -@Timeout(20) -public class ProtonBufferToNetty5AdapterTest { - - private static FileChannel closedChannel; - private static FileChannel channel; - - @BeforeAll - static void setUpChannels(@TempDir Path parentDirectory) throws IOException { - closedChannel = tempFileChannel(parentDirectory); - closedChannel.close(); - channel = tempFileChannel(parentDirectory); - } - - @AfterAll - static void tearDownChannels() throws IOException { - channel.close(); - } - - //----- Tests for basic buffer lifecycle APIs - - @Test - public void testBufferShouldNotBeAccessibleAfterClose() { - try (Buffer buf = createProtonNetty5Buffer(24)) { - buf.writeLong(42); - buf.close(); - verifyInaccessible(buf); - } - } - - //----- Tests for access to buffer bytes by index - - @Test - public void testOffsettedGetOfByteMustBoundsCheckOnNegativeOffset() { - try (Buffer buf = createProtonNetty5Buffer(8)) { - assertThrows(IndexOutOfBoundsException.class, () -> buf.getByte(-1)); - } - } - - @Test - public void testOffsettedGetOfByteReadOnlyMustBoundsCheckOnNegativeOffset() { - try (Buffer buf = createProtonNetty5Buffer(8)) { - assertThrows(IndexOutOfBoundsException.class, () -> buf.makeReadOnly().getByte(-1)); - } - } - - @Test - public void testOffsettedGetOfByteMustNotBoundsCheckWhenReadOffsetAndSizeIsEqualToWriteOffset() { - try (Buffer buf = createProtonNetty5Buffer(8)) { - byte value = 0x01; - buf.writeByte(value); - assertEquals(value, buf.getByte(0)); - } - } - - @Test - public void testOffsettedGetOfByteMustReadWithDefaultEndianByteOrder() { - try (Buffer buf = createProtonNetty5Buffer(8)) { - byte value = 0x01; - buf.writeByte(value); - buf.setByte(0, (byte) 0x10); - assertEquals(0x10, buf.getByte(0)); - } - } - - @Test - public void testOffsettedGetOfByteMustBoundsCheckWhenReadOffsetAndSizeIsGreaterThanWriteOffset() { - try (Buffer buf = createProtonNetty5Buffer(8)) { - byte value = 0x01; - buf.writeByte(value); - assertThrows(IndexOutOfBoundsException.class, () -> buf.getByte(-1)); - } - } - - @Test - public void testOffsettedGetOfByteReadOnlyMustBoundsCheckWhenReadOffsetAndSizeIsGreaterThanWriteOffset() { - try (Buffer buf = createProtonNetty5Buffer(8)) { - byte value = 0x01; - buf.writeByte(value); - assertThrows(IndexOutOfBoundsException.class, () -> buf.makeReadOnly().getByte(-1)); - } - } - - @Test - public void testOffsettedGetOfByteMustNotBoundsCheckWhenReadOffsetIsGreaterThanWriteOffset() { - try (Buffer buf = createProtonNetty5Buffer(8)) { - buf.getByte(0); - } - } - - @Test - public void testOffsettedGetOfByteMustBoundsCheckWhenReadOffsetIsGreaterThanCapacity() { - try (Buffer buf = createProtonNetty5Buffer(8)) { - assertThrows(IndexOutOfBoundsException.class, () -> buf.getByte(8)); - } - } - - @Test - public void testOffsettedGetOfByteReadOnlyMustNotBoundsCheckWhenReadOffsetIsGreaterThanWriteOffset() { - try (Buffer buf = createProtonNetty5Buffer(8)) { - buf.makeReadOnly().getByte(0); - } - } - - @Test - public void testOffsettedGetOfByteReadOnlyMustBoundsCheckWhenReadOffsetIsGreaterThanCapacity() { - try (Buffer buf = createProtonNetty5Buffer(8)) { - assertThrows(IndexOutOfBoundsException.class, () -> buf.makeReadOnly().getByte(8)); - } - } - - @Test - public void testOffsettedGetOfUnsignedByteMustBoundsCheckOnNegativeOffset() { - try (Buffer buf = createProtonNetty5Buffer(8)) { - assertThrows(IndexOutOfBoundsException.class, () -> buf.getUnsignedByte(-1)); - } - } - - @Test - public void testOffsettedGetOfUnsignedByteReadOnlyMustBoundsCheckOnNegativeOffset() { - try (Buffer buf = createProtonNetty5Buffer(8)) { - assertThrows(IndexOutOfBoundsException.class, () -> buf.makeReadOnly().getUnsignedByte(-1)); - } - } - - @Test - public void testOffsettedGetOfUnsignedByteMustNotBoundsCheckWhenReadOffsetAndSizeIsEqualToWriteOffset() { - try (Buffer buf = createProtonNetty5Buffer(8)) { - int value = 0x01; - buf.writeUnsignedByte(value); - assertEquals(value, buf.getUnsignedByte(0)); - } - } - - @Test - public void testOffsettedGetOfUnsignedByteMustReadWithDefaultEndianByteOrder() { - try (Buffer buf = createProtonNetty5Buffer(8)) { - int value = 0x01; - buf.writeUnsignedByte(value); - buf.setByte(0, (byte) 0x10); - assertEquals(0x10, buf.getUnsignedByte(0)); - } - } - - @Test - public void testOffsettedGetOfUnsignedByteMustNotBoundsCheckWhenReadOffsetAndSizeIsGreaterThanWriteOffset() { - try (Buffer buf = createProtonNetty5Buffer(8)) { - int value = 0x01; - buf.writeUnsignedByte(value); - buf.getUnsignedByte(1); - } - } - - @Test - public void testOffsettedGetOfUnsignedByteMustBoundsCheckWhenReadOffsetAndSizeIsGreaterThanCapacity() { - try (Buffer buf = createProtonNetty5Buffer(8)) { - assertThrows(IndexOutOfBoundsException.class, () -> buf.getUnsignedByte(8)); - } - } - - @Test - public void testOffsettedGetOfUnsignedByteReadOnlyMustNotBoundsCheckWhenReadOffsetAndSizeIsGreaterThanWriteOffset() { - try (Buffer buf = createProtonNetty5Buffer(8)) { - int value = 0x01; - buf.writeUnsignedByte(value); - buf.makeReadOnly().getUnsignedByte(1); - } - } - - @Test - public void testOffsettedGetOfUnsignedByteReadOnlyMustBoundsCheckWhenReadOffsetAndSizeIsGreaterThanCapacity() { - try (Buffer buf = createProtonNetty5Buffer(8)) { - assertThrows(IndexOutOfBoundsException.class, () -> buf.makeReadOnly().getUnsignedByte(8)); - } - } - - @Test - public void testOffsettedGetOfUnsignedByteMustNotBoundsCheckWhenReadOffsetIsGreaterThanWriteOffset() { - try (Buffer buf = createProtonNetty5Buffer(8)) { - buf.getUnsignedByte(0); - } - } - - @Test - public void testOffsettedGetOfUnsignedByteMustBoundsCheckWhenReadOffsetIsGreaterThanCapacity() { - try (Buffer buf = createProtonNetty5Buffer(8)) { - assertThrows(IndexOutOfBoundsException.class, () -> buf.getUnsignedByte(8)); - } - } - - @Test - public void testOffsettedGetOfUnsignedByteReadOnlyMustNotBoundsCheckWhenReadOffsetIsGreaterThanWriteOffset() { - try (Buffer buf = createProtonNetty5Buffer(8)) { - buf.makeReadOnly().getUnsignedByte(0); - } - } - - @Test - public void testOffsettedGetOfUnsignedByteReadOnlyMustBoundsCheckWhenReadOffsetIsGreaterThanCapacity() { - try (Buffer buf = createProtonNetty5Buffer(8)) { - assertThrows(IndexOutOfBoundsException.class, () -> buf.makeReadOnly().getUnsignedByte(8)); - } - } - - @SuppressWarnings("resource") - @Test - public void testOffsettedSetOfByteMustBoundsCheckWhenWriteOffsetIsNegative() { - try (Buffer buf = createProtonNetty5Buffer(8).fill((byte) 0)) { - assertEquals(Long.BYTES, buf.capacity()); - byte value = 0x01; - assertThrows(IndexOutOfBoundsException.class, () -> buf.setByte(-1, value)); - buf.writerOffset(Long.BYTES); - // Verify contents are unchanged. - assertEquals(0, buf.readLong()); - } - } - - @SuppressWarnings("resource") - @Test - public void testOffsettedSetOfByteMustBoundsCheckWhenWriteOffsetAndSizeIsBeyondCapacity() { - try (Buffer buf = createProtonNetty5Buffer(8).fill((byte) 0)) { - assertEquals(Long.BYTES, buf.capacity()); - byte value = 0x01; - assertThrows(IndexOutOfBoundsException.class, () -> buf.setByte(8, value)); - buf.writerOffset(Long.BYTES); - // Verify contents are unchanged. - assertEquals(0, buf.readLong()); - } - } - - @Test - public void testOffsettedSetOfByteMustHaveDefaultEndianByteOrder() { - try (Buffer buf = createProtonNetty5Buffer(8).fill((byte) 0)) { - byte value = 0x01; - buf.setByte(0, value); - buf.writerOffset(Long.BYTES); - assertEquals((byte) 0x01, buf.readByte()); - assertEquals((byte) 0x00, buf.readByte()); - assertEquals((byte) 0x00, buf.readByte()); - assertEquals((byte) 0x00, buf.readByte()); - assertEquals((byte) 0x00, buf.readByte()); - assertEquals((byte) 0x00, buf.readByte()); - assertEquals((byte) 0x00, buf.readByte()); - assertEquals((byte) 0x00, buf.readByte()); - } - } - - @SuppressWarnings("resource") - @Test - public void testOffsettedSetOfUnsignedByteMustBoundsCheckWhenWriteOffsetIsNegative() { - try (Buffer buf = createProtonNetty5Buffer(8).fill((byte) 0)) { - assertEquals(Long.BYTES, buf.capacity()); - int value = 0x01; - assertThrows(IndexOutOfBoundsException.class, () -> buf.setUnsignedByte(-1, value)); - buf.writerOffset(Long.BYTES); - // Verify contents are unchanged. - assertEquals(0, buf.readLong()); - } - } - - @SuppressWarnings("resource") - @Test - public void testOffsettedSetOfUnsignedByteMustBoundsCheckWhenWriteOffsetAndSizeIsBeyondCapacity() { - try (Buffer buf = createProtonNetty5Buffer(8).fill((byte) 0)) { - assertEquals(Long.BYTES, buf.capacity()); - int value = 0x01; - assertThrows(IndexOutOfBoundsException.class, () -> buf.setUnsignedByte(8, value)); - buf.writerOffset(Long.BYTES); - // Verify contents are unchanged. - assertEquals(0, buf.readLong()); - } - } - - @Test - public void testOffsettedSetOfUnsignedByteMustHaveDefaultEndianByteOrder() { - try (Buffer buf = createProtonNetty5Buffer(8).fill((byte) 0)) { - int value = 0x01; - buf.setUnsignedByte(0, value); - buf.writerOffset(Long.BYTES); - assertEquals((byte) 0x01, buf.readByte()); - assertEquals((byte) 0x00, buf.readByte()); - assertEquals((byte) 0x00, buf.readByte()); - assertEquals((byte) 0x00, buf.readByte()); - assertEquals((byte) 0x00, buf.readByte()); - assertEquals((byte) 0x00, buf.readByte()); - assertEquals((byte) 0x00, buf.readByte()); - assertEquals((byte) 0x00, buf.readByte()); - } - } - - //----- Test char sequence API - - @Test - public void testReadCharSequence() { - try (Buffer buf = createProtonNetty5Buffer(32)) { - String data = "Hello World"; - buf.writeBytes(data.getBytes(StandardCharsets.US_ASCII)); - assertEquals(data.length(), buf.writerOffset()); - assertEquals(0, buf.readerOffset()); - - final CharSequence charSequence = buf.readCharSequence(data.length(), StandardCharsets.US_ASCII); - assertEquals(data, charSequence.toString()); - assertEquals(data.length(), buf.writerOffset()); - assertEquals(data.length(), buf.readerOffset()); - } - } - - @Test - public void testWriteCharSequence() { - try (Buffer buf = createProtonNetty5Buffer(32)) { - AsciiString data = new AsciiString("Hello world".getBytes(StandardCharsets.US_ASCII)); - buf.writeCharSequence(data, StandardCharsets.US_ASCII); - assertEquals(data.length(), buf.writerOffset()); - assertEquals(0, buf.readerOffset()); - - final byte[] read = readByteArray(buf); - assertEquals(data.toString(), new String(read, StandardCharsets.US_ASCII)); - assertEquals(data.length(), buf.writerOffset()); - assertEquals(data.length(), buf.readerOffset()); - } - } - - @Test - public void testWriteCharSequenceMustExpandCapacityIfBufferIsTooSmall() { - try (Buffer buffer = createProtonNetty5Buffer(4)) { - String string = "Hello World"; - buffer.writeCharSequence(string, StandardCharsets.US_ASCII); - assertTrue(buffer.capacity() >= string.length()); - } - } - - @Test - public void testReadAndWriteCharSequence() { - try (Buffer buf = createProtonNetty5Buffer(32)) { - AsciiString data = new AsciiString("Hello world".getBytes(StandardCharsets.US_ASCII)); - buf.writeCharSequence(data, StandardCharsets.US_ASCII); - assertEquals(data.length(), buf.writerOffset()); - assertEquals(0, buf.readerOffset()); - - final CharSequence read = buf.readCharSequence(data.length(), StandardCharsets.US_ASCII); - assertEquals(data.toString(), read.toString()); - assertEquals(data.length(), buf.writerOffset()); - assertEquals(data.length(), buf.readerOffset()); - } - } - - //----- Tests for the buffer component iteration API - - @Test - public void testFill() { - try (Buffer buf = createProtonNetty5Buffer(16)) { - assertSame(buf, buf.fill((byte) 0xA5)); - buf.writerOffset(16); - assertEquals(0xA5A5A5A5_A5A5A5A5L, buf.readLong()); - assertEquals(0xA5A5A5A5_A5A5A5A5L, buf.readLong()); - } - } - - @Test - public void testComponentCountOfNonCompositeBufferMustBeOne() { - try (Buffer buf = createProtonNetty5Buffer(8)) { - assertEquals(1, buf.countComponents()); - } - } - - @Test - public void testReadableComponentCountMustBeOneIfThereAreReadableBytes() { - try (Buffer buf = createProtonNetty5Buffer(8)) { - assertEquals(0, buf.countReadableComponents()); - buf.writeByte((byte) 1); - assertEquals(1, buf.countReadableComponents()); - } - } - - @Test - public void testWritableComponentCountMustBeOneIfThereAreWritableBytes() { - try (Buffer buf = createProtonNetty5Buffer(8)) { - assertEquals(1, buf.countWritableComponents()); - buf.writeLong(1); - assertEquals(0, buf.countWritableComponents()); - } - } - - @Test - public void compositeBufferComponentCountMustBeTransitiveSum() { - try (ProtonBufferAllocator allocator = ProtonBufferAllocator.defaultAllocator()) { - try (ProtonBuffer a = allocator.allocate(8); - ProtonBuffer b = allocator.allocate(8); - ProtonBuffer c = allocator.allocate(8); - ProtonBuffer x = allocator.composite(new ProtonBuffer[] { b, c }); - ProtonBuffer composite = allocator.composite(new ProtonBuffer[] { a, x }); - ProtonBufferToNetty5Adapter buf = new ProtonBufferToNetty5Adapter(composite)) { - - assertEquals(3, buf.countComponents()); - assertEquals(0, buf.countReadableComponents()); - assertEquals(3, buf.countWritableComponents()); - buf.writeInt(1); - assertEquals(1, buf.countReadableComponents()); - assertEquals(3, buf.countWritableComponents()); - buf.writeInt(1); - assertEquals(1, buf.countReadableComponents()); - assertEquals(2, buf.countWritableComponents()); - buf.writeInt(1); - assertEquals(2, buf.countReadableComponents()); - assertEquals(2, buf.countWritableComponents()); - buf.writeInt(1); - assertEquals(2, buf.countReadableComponents()); - assertEquals(1, buf.countWritableComponents()); - buf.writeInt(1); - assertEquals(3, buf.countReadableComponents()); - assertEquals(1, buf.countWritableComponents()); - buf.writeInt(1); - assertEquals(3, buf.countReadableComponents()); - assertEquals(0, buf.countWritableComponents()); - } - } - } - - @Test - public void testForEachComponentMustVisitBuffer() { - final long value = 0x0102030405060708L; - try (Buffer bufBERW = createProtonNetty5Buffer(8).writeLong(value); - Buffer bufBERO = createProtonNetty5Buffer(8).writeLong(value).makeReadOnly()) { - - verifyReadingForEachSingleComponent(bufBERW); - verifyReadingForEachSingleComponent(bufBERO); - } - } - - @Test - public void testForEachComponentMustVisitAllReadableConstituentBuffersInOrder() { - try (ProtonBufferAllocator allocator = ProtonBufferAllocator.defaultAllocator(); - ProtonBuffer a = allocator.allocate(4).writeInt(1); - ProtonBuffer b = allocator.allocate(4).writeInt(2); - ProtonBuffer c = allocator.allocate(4).writeInt(3); - ProtonBuffer protonComposite = allocator.composite(new ProtonBuffer[] { a, b, c }); - ProtonBufferToNetty5Adapter composite = new ProtonBufferToNetty5Adapter(protonComposite)) { - - LinkedList list = new LinkedList(List.of(1, 2, 3)); - int index = 0; - try (var iterator = composite.forEachComponent()) { - for (var component = iterator.first(); component != null; component = component.next()) { - var buffer = component.readableBuffer(); - int bufferValue = buffer.getInt(); - int expectedValue = list.pollFirst().intValue(); - assertEquals(expectedValue, bufferValue); - assertEquals(bufferValue, index + 1); - assertThrows(ReadOnlyBufferException.class, () -> buffer.put(0, (byte) 0xFF)); - var writableBuffer = InternalBufferUtils.tryGetWritableBufferFromReadableComponent(component); - if (writableBuffer != null) { - int pos = writableBuffer.position(); - bufferValue = writableBuffer.getInt(); - assertEquals(expectedValue, bufferValue); - assertEquals(bufferValue, index + 1); - writableBuffer.put(pos, (byte) 0xFF); - assertEquals((byte) 0xFF, writableBuffer.get(pos)); - } - index++; - } - } - assertEquals(3, index); - assertTrue(list.isEmpty()); - } - } - - @Test - public void testForEachComponentOnClosedBufferMustThrow() { - try (Buffer writableBuffer = createProtonNetty5Buffer(8); - Buffer readableBuffer = createProtonNetty5Buffer(8)) { - - writableBuffer.close(); - assertThrows(BufferClosedException.class, () -> writableBuffer.forEachComponent()); - - readableBuffer.writeLong(0); - readableBuffer.close(); - assertThrows(BufferClosedException.class, () -> readableBuffer.forEachComponent()); - } - } - - @Test - public void testForEachComponentMustAllowCollectingBuffersInArray() { - try (ProtonBufferAllocator allocator = ProtonBufferAllocator.defaultAllocator()) { - try (ProtonBuffer a = allocator.allocate(4); - ProtonBuffer b = allocator.allocate(4); - ProtonBuffer c = allocator.allocate(4); - ProtonBuffer protonComposite = allocator.composite(new ProtonBuffer[] { a, b, c }); - Buffer composite = new ProtonBufferToNetty5Adapter(protonComposite)) { - - int i = 1; - while (composite.writableBytes() > 0) { - composite.writeByte((byte) i++); - } - ByteBuffer[] buffers = new ByteBuffer[composite.countComponents()]; - try (var iterator = composite.forEachComponent()) { - int index = 0; - for (var component = iterator.first(); component != null; component = component.next()) { - buffers[index] = component.readableBuffer(); - index++; - } - } - i = 1; - assertTrue(buffers.length >= 1); - for (ByteBuffer buffer : buffers) { - while (buffer.hasRemaining()) { - assertEquals((byte) i++, buffer.get()); - } - } - } - - try (ProtonBuffer singleBuffer = allocator.allocate(4); - Buffer single = new ProtonBufferToNetty5Adapter(singleBuffer)) { - ByteBuffer[] buffers = new ByteBuffer[single.countComponents()]; - try (var iterator = single.forEachComponent()) { - int index = 0; - for (var component = iterator.first(); component != null; component = component.next()) { - buffers[index] = component.writableBuffer(); - index++; - } - } - assertTrue(buffers.length >= 1); - int i = 1; - for (ByteBuffer buffer : buffers) { - while (buffer.hasRemaining()) { - buffer.put((byte) i++); - } - } - single.writerOffset(single.capacity()); - i = 1; - while (single.readableBytes() > 0) { - assertEquals((byte) i++, single.readByte()); - } - } - } - } - - @Test - public void testForEachComponentMustExposeByteCursors() { - try (ProtonBufferAllocator allocator = ProtonBufferAllocator.defaultAllocator(); - ProtonBuffer inner = allocator.allocate(20); - Buffer buf = new ProtonBufferToNetty5Adapter(inner)) { - - buf.writeLong(0x0102030405060708L); - buf.writeLong(0x1112131415161718L); - assertEquals(0x01020304, buf.readInt()); - try (ProtonBuffer innerActual = allocator.allocate(buf.readableBytes()); - Buffer actualData = new ProtonBufferToNetty5Adapter(innerActual); - ProtonBuffer innerExpected = allocator.allocate(12); - Buffer expectedData = new ProtonBufferToNetty5Adapter(innerExpected)) { - - expectedData.writeInt(0x05060708); - expectedData.writeInt(0x11121314); - expectedData.writeInt(0x15161718); - - try (var iterator = buf.forEachComponent()) { - for (var component = iterator.first(); component != null; component = component.next()) { - ByteCursor forward = component.openCursor(); - while (forward.readByte()) { - actualData.writeByte(forward.getByte()); - } - } - } - - assertEquals(expectedData.readableBytes(), actualData.readableBytes()); - while (expectedData.readableBytes() > 0) { - assertEquals(expectedData.readByte(), actualData.readByte()); - } - } - } - } - - @Test - public void testForEachComponentMustExposeByteCursorsPartial() { - try (ProtonBufferAllocator allocator = ProtonBufferAllocator.defaultAllocator(); - ProtonBuffer inner = allocator.allocate(32); - Buffer buf = new ProtonBufferToNetty5Adapter(inner)) { - - buf.writeLong(0x0102030405060708L); - buf.writeLong(0x1112131415161718L); - assertEquals(0x01020304, buf.readInt()); - try (ProtonBuffer innerActual = allocator.allocate(buf.readableBytes()); - Buffer actualData = new ProtonBufferToNetty5Adapter(innerActual); - ProtonBuffer innerExpected = allocator.allocate(12); - Buffer expectedData = new ProtonBufferToNetty5Adapter(innerExpected)) { - - expectedData.writeInt(0x05060708); - expectedData.writeInt(0x11121314); - expectedData.writeInt(0x15161718); - - try (var iterator = buf.forEachComponent()) { - for (var component = iterator.first(); component != null; component = component.next()) { - ByteCursor forward = component.openCursor(); - while (forward.readByte()) { - actualData.writeByte(forward.getByte()); - } - } - } - - assertEquals(expectedData.readableBytes(), actualData.readableBytes()); - while (expectedData.readableBytes() > 0) { - assertEquals(expectedData.readByte(), actualData.readByte()); - } - } - } - } - - @Test - public void testForEachComponentMustReturnNullFirstWhenNotReadable() { - try (ProtonBufferAllocator allocator = ProtonBufferAllocator.defaultAllocator(); - ProtonBuffer inner = allocator.allocate(0); - Buffer buf = new ProtonBufferToNetty5Adapter(inner)) { - - try (var iterator = buf.forEachComponent()) { - // First component may or may not be null. - var component = iterator.first(); - if (component != null) { - assertEquals(0, component.readableBytes()); - assertEquals(0, component.readableArrayLength()); - ByteBuffer byteBuffer = component.readableBuffer(); - assertEquals(0, byteBuffer.remaining()); - assertEquals(0, byteBuffer.capacity()); - assertNull(component.next()); - } - } - try (var iterator = buf.forEachComponent()) { - // First *readable* component is definitely null. - assertNull(iterator.firstReadable()); - } - } - } - - @Test - public void testForEachWritableMustVisitBuffer() { - try (Buffer bufBERW = createProtonNetty5Buffer(8)) { - verifyWritingForEachSingleComponent(bufBERW); - } - } - - @Test - public void testForEachComponentMustVisitAllWritableConstituentBuffersInOrder() { - try (ProtonBufferAllocator allocator = ProtonBufferAllocator.defaultAllocator(); - ProtonBuffer a = allocator.allocate(8); - ProtonBuffer b = allocator.allocate(8); - ProtonBuffer c = allocator.allocate(8); - ProtonBuffer protonComposite = allocator.composite(new ProtonBuffer[] { a, b, c }); - ProtonBufferToNetty5Adapter buf = new ProtonBufferToNetty5Adapter(protonComposite)) { - - try (var iterator = buf.forEachComponent()) { - int index = 0; - for (var component = iterator.first(); component != null; component = component.next()) { - component.writableBuffer().putLong(0x0102030405060708L + 0x1010101010101010L * index); - index++; - } - } - buf.writerOffset(3 * 8); - assertEquals(0x0102030405060708L, buf.readLong()); - assertEquals(0x1112131415161718L, buf.readLong()); - assertEquals(0x2122232425262728L, buf.readLong()); - } - } - - @Test - public void testForEachComponentChangesMadeToByteBufferComponentMustBeReflectedInBuffer() { - try (Buffer buf = createProtonNetty5Buffer(9)) { - buf.writeByte((byte) 0xFF); - AtomicInteger writtenCounter = new AtomicInteger(); - try (var iterator = buf.forEachComponent()) { - for (var component = iterator.first(); component != null; component = component.next()) { - ByteBuffer buffer = component.writableBuffer(); - while (buffer.hasRemaining()) { - buffer.put((byte) writtenCounter.incrementAndGet()); - } - } - } - buf.writerOffset(9); - assertEquals((byte) 0xFF, buf.readByte()); - assertEquals(0x0102030405060708L, buf.readLong()); - } - } - - @Test - public void testForEachComponentMustHaveZeroWritableBytesWhenNotWritable() { - try (Buffer buf = createProtonNetty5Buffer(0)) { - try (var iterator = buf.forEachComponent()) { - // First component may or may not be null. - var component = iterator.first(); - if (component != null) { - assertEquals(0, component.writableBytes()); - assertEquals(0, component.writableArrayLength()); - ByteBuffer byteBuffer = component.writableBuffer(); - assertEquals(0, byteBuffer.remaining()); - assertEquals(0, byteBuffer.capacity()); - assertNull(component.next()); - } - } - try (var iterator = buf.forEachComponent()) { - // First *writable* component is definitely null. - assertNull(iterator.firstWritable()); - } - } - } - - @Test - public void testChangesMadeToByteBufferComponentsInIterationShouldBeReflectedInBuffer() { - try (Buffer buf = createProtonNetty5Buffer(8)) { - AtomicInteger counter = new AtomicInteger(); - try (var iterator = buf.forEachComponent()) { - for (var component = iterator.first(); component != null; component = component.next()) { - ByteBuffer buffer = component.writableBuffer(); - while (buffer.hasRemaining()) { - buffer.put((byte) counter.incrementAndGet()); - } - } - } - buf.writerOffset(buf.capacity()); - for (int i = 0; i < 8; i++) { - assertEquals((byte) i + 1, buf.getByte(i)); - } - } - } - - @Test - public void testComponentsOfReadOnlyBufferMustNotHaveAnyWritableBytes() { - try (Buffer buf = createProtonNetty5Buffer(8).makeReadOnly()) { - try (var iteration = buf.forEachComponent()) { - for (var c = iteration.first(); c != null; c = c.next()) { - assertEquals(0, c.writableBytes()); - assertEquals(0, c.writableArrayLength()); - ByteBuffer byteBuffer = c.writableBuffer(); - assertEquals(0, byteBuffer.remaining()); - assertEquals(0, byteBuffer.capacity()); - } - } - try (var iteration = buf.forEachComponent()) { - assertNull(iteration.firstWritable()); - } - } - } - - @Test - public void forEachComponentMustBeAbleToIncrementReaderOffset() { - try (Buffer buf = createProtonNetty5Buffer(8); - Buffer target = createProtonNetty5Buffer(5)) { - - buf.writeLong(0x0102030405060708L); - try (var iterator = buf.forEachComponent()) { - for (var component = iterator.first(); component != null; component = component.next()) { - while (target.writableBytes() > 0 && component.readableBytes() > 0) { - ByteBuffer byteBuffer = component.readableBuffer(); - byte value = byteBuffer.get(); - byteBuffer.clear(); - target.writeByte(value); - var cmp = component; // Capture for lambda. - assertThrows(IndexOutOfBoundsException.class, () -> cmp.skipReadableBytes(9)); - component.skipReadableBytes(0); - component.skipReadableBytes(1); - } - } - } - - assertEquals(5, buf.readerOffset()); - assertEquals(3, buf.readableBytes()); - assertEquals(5, target.readableBytes()); - - try (Buffer expected = BufferAllocator.onHeapUnpooled().copyOf(new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05 })) { - assertEquals(target, expected); - } - try (Buffer expected = BufferAllocator.onHeapUnpooled().copyOf(new byte[] { 0x06, 0x07, 0x08 })) { - assertEquals(buf, expected); - } - } - } - - @Test - public void testForEachComponentMustBeAbleToIncrementWriterOffset() { - try (Buffer buf = createProtonNetty5Buffer(8).writeLong(0x0102030405060708L); - Buffer target = buf.copy()) { - - buf.writerOffset(0); // Prime the buffer with data, but leave the write-offset at zero. - try (var iterator = buf.forEachComponent()) { - for (var component = iterator.first(); component != null; component = component.next()) { - while (component.writableBytes() > 0) { - ByteBuffer byteBuffer = component.writableBuffer(); - byte value = byteBuffer.get(); - byteBuffer.clear(); - assertEquals(value, target.readByte()); - var cmp = component; // Capture for lambda. - assertThrows(IndexOutOfBoundsException.class, () -> cmp.skipWritableBytes(9)); - component.skipWritableBytes(0); - component.skipWritableBytes(1); - } - } - } - - assertEquals(8, buf.writerOffset()); - assertEquals(8, target.readerOffset()); - target.readerOffset(0); - assertEquals(buf, target); - } - } - - @Test - public void negativeSkipReadableOnReadableComponentMustThrow() { - try (Buffer buf = createProtonNetty5Buffer(8)) { - buf.writeLong(0x0102030405060708L); - assertEquals(0x01020304, buf.readInt()); - try (var iterator = buf.forEachComponent()) { - for (var component = iterator.first(); component != null; component = component.next()) { - var cmp = component; // Capture for lambda. - assertThrows(IllegalArgumentException.class, () -> cmp.skipReadableBytes(-1)); - } - } - } - } - - @Test - public void negativeSkipWritableOnWritableComponentMustThrow() { - try (Buffer buf = createProtonNetty5Buffer(8)) { - try (var iterator = buf.forEachComponent()) { - for (var component = iterator.first(); component != null; component = component.next()) { - var cmp = component; // Capture for lambda. - assertThrows(IllegalArgumentException.class, () -> cmp.skipWritableBytes(-1)); - } - } - } - } - - //----- Test for channel API methods - - @Test - public void testTransferToMustThrowIfBufferIsClosed() throws IOException { - long position = channel.position(); - long size = channel.size(); - Buffer empty = createProtonNetty5Buffer(8); - empty.close(); - assertThrows(BufferClosedException.class, () -> empty.transferTo(channel, 8)); - assertEquals(position, channel.position()); - assertEquals(size, channel.size()); - Buffer withData = createProtonNetty5Buffer(8); - withData.writeLong(0x0102030405060708L); - withData.close(); - assertThrows(BufferClosedException.class, () -> withData.transferTo(channel, 8)); - assertEquals(position, channel.position()); - assertEquals(size, channel.size()); - } - - @Test - public void testTransferToMustCapAtReadableBytes() throws IOException { - try (Buffer buf = createProtonNetty5Buffer(8)) { - buf.writeLong(0x0102030405060708L); - buf.writerOffset(buf.writerOffset() - 5); - long position = channel.position(); - long size = channel.size(); - int bytesWritten = buf.transferTo(channel, 8); - - assertEquals(3, bytesWritten); - assertEquals(3 + position, channel.position()); - assertEquals(3 + size, channel.size()); - assertEquals(5, buf.writableBytes()); - assertEquals(0, buf.readableBytes()); - } - } - - @Test - public void testTransferToMustCapAtLength() throws IOException { - try (Buffer buf = createProtonNetty5Buffer(8)) { - buf.writeLong(0x0102030405060708L); - long position = channel.position(); - long size = channel.size(); - int bytesWritten = buf.transferTo(channel, 3); - assertEquals(3, bytesWritten); - assertEquals(3 + position, channel.position()); - assertEquals(3 + size, channel.size()); - assertEquals(5, buf.readableBytes()); - } - } - - @Test - public void testTransferToMustThrowIfChannelIsClosed() throws IOException { - try (Buffer buf = createProtonNetty5Buffer(8)) { - buf.writeLong(0x0102030405060708L); - assertThrows(ClosedChannelException.class, () -> buf.transferTo(closedChannel, 8)); - assertTrue(buf.isAccessible()); - assertEquals(8, buf.readableBytes()); - } - } - - @Test - public void testTransferToMustThrowIfChannelIsNull() throws IOException { - try (Buffer buf = createProtonNetty5Buffer(8)) { - buf.writeLong(0x0102030405060708L); - assertThrows(NullPointerException.class, () -> buf.transferTo(null, 8)); - assertTrue(buf.isAccessible()); - assertEquals(8, buf.readableBytes()); - } - } - - @Test - public void testTransferToMustThrowIfLengthIsNegative() throws IOException { - try (Buffer buf = createProtonNetty5Buffer(8)) { - buf.writeLong(0x0102030405060708L); - assertThrows(IllegalArgumentException.class, () -> buf.transferTo(channel, -1)); - assertEquals(8, buf.readableBytes()); - } - } - - @Test - public void transferToMustIgnoreZeroLengthOperations() throws IOException { - try (Buffer buf = createProtonNetty5Buffer(8)) { - long position = channel.position(); - long size = channel.size(); - buf.writeLong(0x0102030405060708L); - int bytesWritten = buf.transferTo(channel, 0); - assertEquals(8, buf.readableBytes()); - assertEquals(0, bytesWritten); - assertEquals(position, channel.position()); - assertEquals(size, channel.size()); - } - } - - @Test - public void testTransferToMustMoveDataToChannel() throws IOException { - try (Buffer buf = createProtonNetty5Buffer(8)) { - long value = ThreadLocalRandom.current().nextLong(); - buf.writeLong(value); - long position = channel.position(); - int bytesWritten = buf.transferTo(channel, 8); - assertEquals(8, bytesWritten); - ByteBuffer buffer = ByteBuffer.allocate(8); - final int bytesRead = channel.read(buffer, position); - assertEquals(8, bytesRead); - buffer.flip(); - assertEquals(value, buffer.getLong()); - } - } - - @Test - public void testTransferToMustMoveReadOnlyDataToChannel() throws IOException { - try (Buffer buf = createProtonNetty5Buffer(8)) { - long value = ThreadLocalRandom.current().nextLong(); - buf.writeLong(value).makeReadOnly(); - long position = channel.position(); - int bytesWritten = buf.transferTo(channel, 8); - assertEquals(8, bytesWritten); - ByteBuffer buffer = ByteBuffer.allocate(8); - int bytesRead = channel.read(buffer, position); - assertEquals(8, bytesRead); - buffer.flip(); - assertEquals(value, buffer.getLong()); - } - } - - @Test - public void testTransferToZeroBytesMustNotThrowOnClosedChannel() throws IOException { - try (Buffer empty = createProtonNetty5Buffer(0); - Buffer notEmpty = createProtonNetty5Buffer(4).writeInt(42)) { - - empty.transferTo(closedChannel, 4); - notEmpty.transferTo(closedChannel, 0); - } - } - - @Test - public void testTransferFromMustThrowIfBufferIsClosed() throws IOException { - doTransferFromMustThrowIfBufferIsClosed(false); - } - - @Test - public void testTransferFromWithPositionMustThrowIfBufferIsClosed() throws IOException { - doTransferFromMustThrowIfBufferIsClosed(true); - } - - private static void doTransferFromMustThrowIfBufferIsClosed(boolean withPosition) throws IOException { - try { - ByteBuffer data = ByteBuffer.allocate(8).putLong(0x0102030405060708L).flip(); - long position = channel.position(); - assertEquals(8, channel.write(data, position)); - long size = channel.size(); - Buffer empty = createProtonNetty5Buffer(0); - empty.close(); - assertThrows(BufferClosedException.class, () -> { - if (withPosition) { - empty.transferFrom(channel, 4 + position, 8); - } else { - empty.transferFrom(channel, 8); - } - }); - assertEquals(position, channel.position()); - assertEquals(size, channel.size()); - Buffer withAvailableSpace = createProtonNetty5Buffer(8); - withAvailableSpace.close(); - assertThrows(BufferClosedException.class, () -> { - if (withPosition) { - withAvailableSpace.transferFrom(channel, 4 + position, 8); - } else { - withAvailableSpace.transferFrom(channel, 8); - } - }); - assertEquals(position, channel.position()); - assertEquals(size, channel.size()); - } finally { - channel.position(channel.size()); - } - } - - @Test - public void testTransferFromMustCapAtWritableBytes() throws IOException { - doTransferFromMustCapAtWritableBytes(false); - } - - @Test - public void testTransferFromWithPositionMustCapAtWritableBytes() throws IOException { - doTransferFromMustCapAtWritableBytes(true); - } - - private static void doTransferFromMustCapAtWritableBytes(boolean withPosition) throws IOException { - try (Buffer buf = createProtonNetty5Buffer(3)) { - ByteBuffer data = ByteBuffer.allocate(8).putLong(0x0102030405060708L).flip(); - long position = channel.position(); - assertEquals(8, channel.write(data, position)); - long size = channel.size(); - int bytesRead = withPosition ? buf.transferFrom(channel, 4 + position, 8) : buf.transferFrom(channel, 8); - assertEquals(3, bytesRead); - assertEquals(withPosition ? position : 3 + position, channel.position()); - assertEquals(size, channel.size()); - assertEquals(0, buf.writableBytes()); - assertEquals(3, buf.readableBytes()); - } finally { - channel.position(channel.size()); - } - } - - @Test - public void testTransferFromMustCapAtLength() throws IOException { - doTransferFromMustCapAtLength(false); - } - - @Test - public void testTransferFromWithPositionMustCapAtLength() throws IOException { - doTransferFromMustCapAtLength(true); - } - - private static void doTransferFromMustCapAtLength(boolean withPosition) throws IOException { - try (Buffer buf = createProtonNetty5Buffer(8)) { - ByteBuffer data = ByteBuffer.allocate(8).putLong(0x0102030405060708L).flip(); - long position = channel.position(); - assertEquals(8, channel.write(data, position)); - long size = channel.size(); - int bytesRead = withPosition ? buf.transferFrom(channel, 4 + position, 3) : buf.transferFrom(channel, 3); - assertEquals(3, bytesRead); - assertEquals(withPosition ? position : 3 + position, channel.position()); - assertEquals(size, channel.size()); - assertEquals(5, buf.writableBytes()); - assertEquals(3, buf.readableBytes()); - } finally { - channel.position(channel.size()); - } - } - - @Test - public void testTransferFromMustThrowIfChannelIsClosed() { - doTransferFromMustThrowIfChannelIsClosed(false); - } - - @Test - public void testTransferFromWithPositionMustThrowIfChannelIsClosed() { - doTransferFromMustThrowIfChannelIsClosed(true); - } - - private static void doTransferFromMustThrowIfChannelIsClosed(boolean withPosition) { - try (Buffer buf = createProtonNetty5Buffer(8)) { - assertThrows(ClosedChannelException.class, () -> { - if (withPosition) { - buf.transferFrom(closedChannel, 4, 8); - } else { - buf.transferFrom(closedChannel, 8); - } - }); - assertTrue(buf.isAccessible()); - assertEquals(8, buf.writableBytes()); - } - } - - @Test - public void testTransferFromMustThrowIfChannelIsNull() { - doTransferFromMustThrowIfChannelIsNull(false); - } - - @Test - public void testTransferFromWithPositionMustThrowIfChannelIsNull() { - doTransferFromMustThrowIfChannelIsNull(true); - } - - private static void doTransferFromMustThrowIfChannelIsNull(boolean withPosition) { - try (Buffer buf = createProtonNetty5Buffer(8)) { - assertThrows(NullPointerException.class, () -> { - if (withPosition) { - buf.transferFrom(null, 4, 8); - } else { - buf.transferFrom(null, 8); - } - }); - assertTrue(buf.isAccessible()); - assertEquals(8, buf.writableBytes()); - } - } - - @Test - public void testTransferFromMustThrowIfLengthIsNegative() throws IOException { - doTransferFromMustThrowIfLengthIsNegative(false); - } - - @Test - public void testTransferFromWithPositionMustThrowIfLengthIsNegative() throws IOException { - doTransferFromMustThrowIfLengthIsNegative(true); - } - - private static void doTransferFromMustThrowIfLengthIsNegative(boolean withPosition) throws IOException { - try (Buffer buf = createProtonNetty5Buffer(8)) { - long position = channel.position(); - ByteBuffer data = ByteBuffer.allocate(8).putLong(0x0102030405060708L).flip(); - assertEquals(8, channel.write(data, position)); - long size = channel.size(); - assertThrows(IllegalArgumentException.class, () -> { - if (withPosition) { - buf.transferFrom(channel, 4 + position, -1); - } else { - buf.transferFrom(channel, -1); - } - }); - assertTrue(buf.isAccessible()); - assertEquals(8, buf.writableBytes()); - assertEquals(position, channel.position()); - assertEquals(size, channel.size()); - } finally { - channel.position(channel.size()); - } - } - - @Test - public void testTransferFromMustIgnoreZeroLengthOperations() throws IOException { - doTransferFromMustIgnoreZeroLengthOperations(false); - } - - @Test - public void testTransferFromWithPositionMustIgnoreZeroLengthOperations() throws IOException { - doTransferFromMustIgnoreZeroLengthOperations(true); - } - - private static void doTransferFromMustIgnoreZeroLengthOperations(boolean withPosition) throws IOException { - try (Buffer buf = createProtonNetty5Buffer(8)) { - long position = channel.position(); - ByteBuffer data = ByteBuffer.allocate(8).putLong(0x0102030405060708L).flip(); - assertEquals(8, channel.write(data, position)); - long size = channel.size(); - int bytesRead = withPosition ? buf.transferFrom(channel, 4 + position, 0) : buf.transferFrom(channel, 0); - assertEquals(0, bytesRead); - assertEquals(0, buf.readableBytes()); - assertEquals(8, buf.writableBytes()); - assertEquals(position, channel.position()); - assertEquals(size, channel.size()); - } finally { - channel.position(channel.size()); - } - } - - @Test - public void testTransferFromMustMoveDataFromChannel() throws IOException { - doTransferFromMustMoveDataFromChannel(false); - } - - @Test - public void testTransferFromWithPositionMustMoveDataFromChannel() throws IOException { - doTransferFromMustMoveDataFromChannel(true); - } - - private static void doTransferFromMustMoveDataFromChannel(boolean withPosition) throws IOException { - try (Buffer buf = createProtonNetty5Buffer(8)) { - long value = ThreadLocalRandom.current().nextLong(); - ByteBuffer data = ByteBuffer.allocate(8).putLong(value).flip(); - long position = channel.position(); - assertEquals(8, channel.write(data, position)); - long size = channel.size(); - int bytesRead = withPosition ? buf.transferFrom(channel, position, 8) : buf.transferFrom(channel, 8); - assertEquals(8, bytesRead); - assertEquals(8, buf.readableBytes()); - assertEquals(0, buf.writableBytes()); - assertEquals(withPosition ? position : 8 + position, channel.position()); - assertEquals(size, channel.size()); - assertEquals(value, buf.readLong()); - } finally { - channel.position(channel.size()); - } - } - - @Test - public void testTransferFromMustNotReadBeyondEndOfChannel() throws IOException { - doTransferFromMustNotReadBeyondEndOfChannel(false); - } - - @Test - public void testTransferFromWithPositionMustNotReadBeyondEndOfChannel() throws IOException { - doTransferFromMustNotReadBeyondEndOfChannel(true); - } - - private static void doTransferFromMustNotReadBeyondEndOfChannel(boolean withPosition) throws IOException { - try (Buffer buf = createProtonNetty5Buffer(8)) { - ByteBuffer data = ByteBuffer.allocate(8).putInt(0x01020304).flip(); - long position = channel.position(); - assertEquals(4, channel.write(data, position)); - long size = channel.size(); - int bytesRead = withPosition ? buf.transferFrom(channel, position, 8) : buf.transferFrom(channel, 8); - assertEquals(4, bytesRead); - assertEquals(4, buf.readableBytes()); - assertEquals(withPosition ? position : 4 + position, channel.position()); - assertEquals(size, channel.size()); - } finally { - channel.position(channel.size()); - } - } - - @Test - public void testTransferFromMustReturnMinusOneForEndOfStream() throws IOException { - doTransferFromMustReturnMinusOneForEndOfStream(false); - } - - @Test - public void testTransferFromWithPositionMustReturnMinusOneForEndOfStream() throws IOException { - doTransferFromMustReturnMinusOneForEndOfStream(true); - } - - private static void doTransferFromMustReturnMinusOneForEndOfStream(boolean withPosition) throws IOException { - try (Buffer buf = createProtonNetty5Buffer(8)) { - long position = channel.position(); - long size = channel.size(); - int bytesRead = withPosition ? buf.transferFrom(channel, position, 8) : buf.transferFrom(channel, 8); - assertEquals(-1, bytesRead); - assertEquals(0, buf.readableBytes()); - assertEquals(position, channel.position()); - assertEquals(size, channel.size()); - } finally { - channel.position(channel.size()); - } - } - - @Test - public void testTransferFromMustReturnMinusOneForEndOfStreamNonScattering() throws IOException { - try (Buffer buf = createProtonNetty5Buffer(8)) { - long position = channel.position(); - long size = channel.size(); - ReadableByteChannel nonScatteringChannel = new ReadableByteChannel() { - @Override - public int read(ByteBuffer dst) throws IOException { - return channel.read(dst); - } - - @Override - public boolean isOpen() { - return channel.isOpen(); - } - - @Override - public void close() throws IOException { - channel.close(); - } - }; - final int bytesRead = buf.transferFrom(nonScatteringChannel, 8); - assertEquals(-1, bytesRead); - assertEquals(0, buf.readableBytes()); - assertEquals(position, channel.position()); - assertEquals(size, channel.size()); - } finally { - channel.position(channel.size()); - } - } - - @Test - public void testTransferFromMustStartFromWritableOffset() throws IOException { - doTransferFromMustStartFromWritableOffset(false); - } - - @Test - public void testTransferFromWithPositionMustStartFromWritableOffset() throws IOException { - doTransferFromMustStartFromWritableOffset(true); - } - - private static void doTransferFromMustStartFromWritableOffset(boolean withPosition) throws IOException { - try (Buffer buf = createProtonNetty5Buffer(4)) { - ByteBuffer data = ByteBuffer.allocate(4).putInt(0x01020304).flip(); - long position = channel.position(); - assertEquals(4, channel.write(data, position)); - long size = channel.size(); - int bytesRead = withPosition ? buf.transferFrom(channel, position, 2) : buf.transferFrom(channel, 2); - bytesRead += withPosition ? buf.transferFrom(channel, 2 + position, 2) : buf.transferFrom(channel, 2); - assertEquals(4, bytesRead); - assertEquals(4, buf.readableBytes()); - assertEquals(withPosition ? position : 4 + position, channel.position()); - assertEquals(size, channel.size()); - for (int i = 0; i < buf.readableBytes(); i++) { - assertEquals(data.get(i), buf.readByte()); - } - } finally { - channel.position(channel.size()); - } - } - - @Test - public void testTransferFromMustThrowIfBufferIsReadOnly() throws IOException { - doTransferFromMustThrowIfBufferIsReadOnly(false); - } - - @Test - public void testTransferFromWithPositionMustThrowIfBufferIsReadOnly() throws IOException { - doTransferFromMustThrowIfBufferIsReadOnly(true); - } - - private static void doTransferFromMustThrowIfBufferIsReadOnly(boolean withPosition) throws IOException { - try (Buffer buf = createProtonNetty5Buffer(8).writeLong(0x0102030405060708L).makeReadOnly()) { - long position = channel.position(); - long size = channel.size(); - assertThrows(BufferReadOnlyException.class, () -> { - if (withPosition) { - buf.transferFrom(channel, 4 + position, 8); - } else { - buf.transferFrom(channel, 8); - } - }); - assertEquals(8, buf.readableBytes()); - assertEquals(position, channel.position()); - assertEquals(size, channel.size()); - } - } - - @Test - public void testTransferFromZeroBytesMustNotThrowOnClosedChannel() throws IOException { - doTransferFromZeroBytesMustNotThrowOnClosedChannel(false); - } - - @Test - public void testTransferFromWithPositionZeroBytesMustNotThrowOnClosedChannel() throws IOException { - doTransferFromZeroBytesMustNotThrowOnClosedChannel(true); - } - - private static void doTransferFromZeroBytesMustNotThrowOnClosedChannel(boolean withPosition) throws IOException { - try (Buffer empty = createProtonNetty5Buffer(0); - Buffer notEmpty = createProtonNetty5Buffer(4)) { - - if (withPosition) { - empty.transferFrom(closedChannel, 4, 4); - notEmpty.transferFrom(closedChannel, 4, 0); - } else { - empty.transferFrom(closedChannel, 4); - notEmpty.transferFrom(closedChannel, 0); - } - } - } - - private static void verifyReadingForEachSingleComponent(Buffer buf) { - try (var iterator = buf.forEachComponent()) { - for (var component = iterator.first(); component != null; component = component.next()) { - ByteBuffer buffer = component.readableBuffer(); - assertEquals(0, buffer.position()); - assertEquals(8, buffer.limit()); - assertEquals(8, buffer.capacity()); - - assertEquals(0x0102030405060708L, buffer.getLong()); - - if (component.hasReadableArray()) { - byte[] array = component.readableArray(); - byte[] arrayCopy = new byte[component.readableArrayLength()]; - System.arraycopy(array, component.readableArrayOffset(), arrayCopy, 0, arrayCopy.length); - if (buffer.order() == BIG_ENDIAN) { - assertArrayEquals(arrayCopy, new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}); - } else { - assertArrayEquals(arrayCopy, new byte[] {0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x0}); - } - } - - assertThrows(ReadOnlyBufferException.class, () -> buffer.put(0, (byte) 0xFF)); - } - } - } - - public static void verifyWritingForEachSingleComponent(Buffer buf) { - buf.fill((byte) 0); - try (var iterator = buf.forEachComponent()) { - for (var component = iterator.first(); component != null; component = component.next()) { - ByteBuffer buffer = component.writableBuffer(); - assertEquals(0, buffer.position()); - assertEquals(8, buffer.limit()); - assertEquals(8, buffer.capacity()); - buffer.putLong(0x0102030405060708L); - buffer.flip(); - assertEquals(0x0102030405060708L, buffer.getLong()); - buf.writerOffset(8); - assertEquals(0x0102030405060708L, buf.getLong(0)); - - assertEquals(0, component.writableNativeAddress()); - - buf.writerOffset(0); - if (component.hasWritableArray()) { - byte[] array = component.writableArray(); - int offset = component.writableArrayOffset(); - byte[] arrayCopy = new byte[component.writableArrayLength()]; - System.arraycopy(array, offset, arrayCopy, 0, arrayCopy.length); - if (buffer.order() == BIG_ENDIAN) { - assertArrayEquals(arrayCopy, new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}); - } else { - assertArrayEquals(arrayCopy, new byte[] {0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x0}); - } - } - - buffer.put(0, (byte) 0xFF); - assertEquals((byte) 0xFF, buffer.get(0)); - assertEquals((byte) 0xFF, buf.getByte(0)); - } - } - } - private static Buffer createProtonNetty5Buffer(int capacity) { - ProtonBuffer protonBuffer = ProtonBufferAllocator.defaultAllocator().allocate(capacity); - ProtonBufferToNetty5Adapter wrapper = new ProtonBufferToNetty5Adapter(protonBuffer); - - return wrapper; - } - - private static FileChannel tempFileChannel(Path parentDirectory) throws IOException { - Path path = Files.createTempFile(parentDirectory, "BufferAndChannelTest", "txt"); - return FileChannel.open(path, READ, WRITE, DELETE_ON_CLOSE); - } - - public static byte[] readByteArray(Buffer buf) { - byte[] bs = new byte[buf.readableBytes()]; - buf.copyInto(buf.readerOffset(), bs, 0, bs.length); - buf.readerOffset(buf.writerOffset()); - return bs; - } - - @SuppressWarnings("resource") - private static void verifyInaccessible(Buffer buf) { - verifyReadInaccessible(buf); - - verifyWriteInaccessible(buf, BufferClosedException.class); - - try (BufferAllocator allocator = BufferAllocator.onHeapUnpooled(); - Buffer target = allocator.allocate(24)) { - assertThrows(BufferClosedException.class, () -> buf.copyInto(0, target, 0, 1)); - assertThrows(BufferClosedException.class, () -> buf.copyInto(0, target, 0, 0)); - assertThrows(BufferClosedException.class, () -> buf.copyInto(0, new byte[1], 0, 1)); - assertThrows(BufferClosedException.class, () -> buf.copyInto(0, new byte[1], 0, 0)); - assertThrows(BufferClosedException.class, () -> buf.copyInto(0, ByteBuffer.allocate(1), 0, 1)); - assertThrows(BufferClosedException.class, () -> buf.copyInto(0, ByteBuffer.allocate(1), 0, 0)); - if (CompositeBuffer.isComposite(buf)) { - assertThrows(BufferClosedException.class, () -> ((CompositeBuffer) buf).extendWith(target.send())); - } - } - - assertThrows(BufferClosedException.class, () -> buf.split()); - assertThrows(BufferClosedException.class, () -> buf.send()); - assertThrows(BufferClosedException.class, () -> buf.copy()); - assertThrows(BufferClosedException.class, () -> buf.openCursor()); - assertThrows(BufferClosedException.class, () -> buf.openCursor(0, 0)); - assertThrows(BufferClosedException.class, () -> buf.openReverseCursor()); - assertThrows(BufferClosedException.class, () -> buf.openReverseCursor(0, 0)); - } - - private static void verifyReadInaccessible(Buffer buf) { - assertThrows(BufferClosedException.class, () -> buf.readBoolean()); - assertThrows(BufferClosedException.class, () -> buf.readByte()); - assertThrows(BufferClosedException.class, () -> buf.readUnsignedByte()); - assertThrows(BufferClosedException.class, () -> buf.readChar()); - assertThrows(BufferClosedException.class, () -> buf.readShort()); - assertThrows(BufferClosedException.class, () -> buf.readUnsignedShort()); - assertThrows(BufferClosedException.class, () -> buf.readMedium()); - assertThrows(BufferClosedException.class, () -> buf.readUnsignedMedium()); - assertThrows(BufferClosedException.class, () -> buf.readInt()); - assertThrows(BufferClosedException.class, () -> buf.readUnsignedInt()); - assertThrows(BufferClosedException.class, () -> buf.readFloat()); - assertThrows(BufferClosedException.class, () -> buf.readLong()); - assertThrows(BufferClosedException.class, () -> buf.readDouble()); - - assertThrows(BufferClosedException.class, () -> buf.getBoolean(0)); - assertThrows(BufferClosedException.class, () -> buf.getByte(0)); - assertThrows(BufferClosedException.class, () -> buf.getUnsignedByte(0)); - assertThrows(BufferClosedException.class, () -> buf.getChar(0)); - assertThrows(BufferClosedException.class, () -> buf.getShort(0)); - assertThrows(BufferClosedException.class, () -> buf.getUnsignedShort(0)); - assertThrows(BufferClosedException.class, () -> buf.getMedium(0)); - assertThrows(BufferClosedException.class, () -> buf.getUnsignedMedium(0)); - assertThrows(BufferClosedException.class, () -> buf.getInt(0)); - assertThrows(BufferClosedException.class, () -> buf.getUnsignedInt(0)); - assertThrows(BufferClosedException.class, () -> buf.getFloat(0)); - assertThrows(BufferClosedException.class, () -> buf.getLong(0)); - assertThrows(BufferClosedException.class, () -> buf.getDouble(0)); - } - - private static void verifyWriteInaccessible(Buffer buf, Class expected) { - assertThrows(expected, () -> buf.writeByte((byte) 32)); - assertThrows(expected, () -> buf.writeUnsignedByte(32)); - assertThrows(expected, () -> buf.writeChar('3')); - assertThrows(expected, () -> buf.writeShort((short) 32)); - assertThrows(expected, () -> buf.writeUnsignedShort(32)); - assertThrows(expected, () -> buf.writeMedium(32)); - assertThrows(expected, () -> buf.writeUnsignedMedium(32)); - assertThrows(expected, () -> buf.writeInt(32)); - assertThrows(expected, () -> buf.writeUnsignedInt(32)); - assertThrows(expected, () -> buf.writeFloat(3.2f)); - assertThrows(expected, () -> buf.writeLong(32)); - assertThrows(expected, () -> buf.writeDouble(32)); - - assertThrows(expected, () -> buf.setByte(0, (byte) 32)); - assertThrows(expected, () -> buf.setUnsignedByte(0, 32)); - assertThrows(expected, () -> buf.setChar(0, '3')); - assertThrows(expected, () -> buf.setShort(0, (short) 32)); - assertThrows(expected, () -> buf.setUnsignedShort(0, 32)); - assertThrows(expected, () -> buf.setMedium(0, 32)); - assertThrows(expected, () -> buf.setUnsignedMedium(0, 32)); - assertThrows(expected, () -> buf.setInt(0, 32)); - assertThrows(expected, () -> buf.setUnsignedInt(0, 32)); - assertThrows(expected, () -> buf.setFloat(0, 3.2f)); - assertThrows(expected, () -> buf.setLong(0, 32)); - assertThrows(expected, () -> buf.setDouble(0, 32)); - - assertThrows(expected, () -> buf.ensureWritable(1)); - assertThrows(expected, () -> buf.fill((byte) 0)); - assertThrows(expected, () -> buf.compact()); - try (BufferAllocator allocator = BufferAllocator.onHeapUnpooled(); - Buffer source = allocator.allocate(8)) { - assertThrows(expected, () -> source.copyInto(0, buf, 0, 1)); - if (expected == BufferClosedException.class) { - assertThrows(expected, () -> buf.copyInto(0, source, 0, 1)); - } - } - } -}