From ba696986acad8aaee7365aa56e90ccef08bce4bc Mon Sep 17 00:00:00 2001 From: yandryakov Date: Thu, 29 Jul 2021 16:07:02 +0300 Subject: [PATCH 1/2] support underflow handling without thread sleep --- .../nio/SocketChannelFrameHandlerState.java | 24 ++++++++++++++--- .../impl/nio/SslEngineFrameBuilder.java | 27 ++++++++++++------- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/rabbitmq/client/impl/nio/SocketChannelFrameHandlerState.java b/src/main/java/com/rabbitmq/client/impl/nio/SocketChannelFrameHandlerState.java index 50f08a59f2..87c1d08928 100644 --- a/src/main/java/com/rabbitmq/client/impl/nio/SocketChannelFrameHandlerState.java +++ b/src/main/java/com/rabbitmq/client/impl/nio/SocketChannelFrameHandlerState.java @@ -26,6 +26,8 @@ import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; +import java.util.concurrent.atomic.AtomicBoolean; + /** * @@ -70,6 +72,8 @@ public class SocketChannelFrameHandlerState { final FrameBuilder frameBuilder; + private final AtomicBoolean isUnderflowHandlingEnabled = new AtomicBoolean(false); + public SocketChannelFrameHandlerState(SocketChannel channel, NioLoopContext nioLoopsState, NioParams nioParams, SSLEngine sslEngine) { this.channel = channel; this.readSelectorState = nioLoopsState.readSelectorState; @@ -105,7 +109,7 @@ public SocketChannelFrameHandlerState(SocketChannel channel, NioLoopContext nioL this.outputStream = new DataOutputStream( new SslEngineByteBufferOutputStream(sslEngine, plainOut, cipherOut, channel) ); - this.frameBuilder = new SslEngineFrameBuilder(sslEngine, plainIn, cipherIn, channel); + this.frameBuilder = new SslEngineFrameBuilder(sslEngine, plainIn, cipherIn, channel, isUnderflowHandlingEnabled); } } @@ -176,11 +180,14 @@ void endWriteSequence() { void prepareForReadSequence() throws IOException { if(ssl) { - cipherIn.clear(); - plainIn.clear(); + if (!isUnderflowHandlingEnabled.get()) { + cipherIn.clear(); + cipherIn.flip(); + } - cipherIn.flip(); + plainIn.clear(); plainIn.flip(); + } else { NioHelper.read(channel, plainIn); plainIn.flip(); @@ -189,6 +196,15 @@ void prepareForReadSequence() throws IOException { boolean continueReading() throws IOException { if(ssl) { + if (isUnderflowHandlingEnabled.get()) { + int bytesRead = NioHelper.read(channel, cipherIn); + if (bytesRead == 0) { + return false; + } else { + cipherIn.flip(); + return true; + } + } if (!plainIn.hasRemaining() && !cipherIn.hasRemaining()) { // need to try to read something cipherIn.clear(); diff --git a/src/main/java/com/rabbitmq/client/impl/nio/SslEngineFrameBuilder.java b/src/main/java/com/rabbitmq/client/impl/nio/SslEngineFrameBuilder.java index c2f1923874..34295fb36a 100644 --- a/src/main/java/com/rabbitmq/client/impl/nio/SslEngineFrameBuilder.java +++ b/src/main/java/com/rabbitmq/client/impl/nio/SslEngineFrameBuilder.java @@ -21,6 +21,8 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ReadableByteChannel; +import java.util.concurrent.atomic.AtomicBoolean; + /** * Sub-class of {@link FrameBuilder} that unwraps crypted data from the network. @@ -32,20 +34,25 @@ public class SslEngineFrameBuilder extends FrameBuilder { private final ByteBuffer cipherBuffer; - public SslEngineFrameBuilder(SSLEngine sslEngine, ByteBuffer plainIn, ByteBuffer cipherIn, ReadableByteChannel channel) { + private final AtomicBoolean isUnderflowHandlingEnabled; + + public SslEngineFrameBuilder(SSLEngine sslEngine, ByteBuffer plainIn, ByteBuffer cipherIn, ReadableByteChannel channel, final AtomicBoolean isUnderflowHandlingEnabled) { super(channel, plainIn); this.sslEngine = sslEngine; this.cipherBuffer = cipherIn; + this.isUnderflowHandlingEnabled = isUnderflowHandlingEnabled; } @Override protected boolean somethingToRead() throws IOException { - if (applicationBuffer.hasRemaining()) { + if (applicationBuffer.hasRemaining() && !isUnderflowHandlingEnabled.get()) { return true; } else { applicationBuffer.clear(); - while (true) { + boolean underflowHandling = false; + + try { SSLEngineResult result = sslEngine.unwrap(cipherBuffer, applicationBuffer); switch (result.getStatus()) { case OK: @@ -59,18 +66,18 @@ protected boolean somethingToRead() throws IOException { throw new SSLException("buffer overflow in read"); case BUFFER_UNDERFLOW: cipherBuffer.compact(); - int read = NioHelper.read(channel, cipherBuffer); - if (read == 0) { - return false; - } - cipherBuffer.flip(); - break; + underflowHandling = true; + return false; case CLOSED: throw new SSLException("closed in read"); default: throw new IllegalStateException("Invalid SSL status: " + result.getStatus()); - } + } + } finally { + isUnderflowHandlingEnabled.set(underflowHandling); } + + return false; } } From 217a5e8c2a8e42748b6a33dadae9443ea7f7119a Mon Sep 17 00:00:00 2001 From: yandryakov Date: Thu, 29 Jul 2021 18:34:47 +0300 Subject: [PATCH 2/2] refactoring - make isUnderflowHandlingEnabled private property --- .../com/rabbitmq/client/impl/nio/FrameBuilder.java | 5 +++++ .../impl/nio/SocketChannelFrameHandlerState.java | 9 +++------ .../client/impl/nio/SslEngineFrameBuilder.java | 14 ++++++++------ 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/rabbitmq/client/impl/nio/FrameBuilder.java b/src/main/java/com/rabbitmq/client/impl/nio/FrameBuilder.java index 1eca94eb77..29dba1df54 100644 --- a/src/main/java/com/rabbitmq/client/impl/nio/FrameBuilder.java +++ b/src/main/java/com/rabbitmq/client/impl/nio/FrameBuilder.java @@ -200,4 +200,9 @@ private void handleProtocolVersionMismatch() throws IOException { } throw x; } + + //Indicates ssl underflow state - means that cipherBuffer should aggregate next chunks of bytes + public boolean isUnderflowHandlingEnabled() { + return false; + } } diff --git a/src/main/java/com/rabbitmq/client/impl/nio/SocketChannelFrameHandlerState.java b/src/main/java/com/rabbitmq/client/impl/nio/SocketChannelFrameHandlerState.java index 87c1d08928..4f1e4dc885 100644 --- a/src/main/java/com/rabbitmq/client/impl/nio/SocketChannelFrameHandlerState.java +++ b/src/main/java/com/rabbitmq/client/impl/nio/SocketChannelFrameHandlerState.java @@ -26,7 +26,6 @@ import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; -import java.util.concurrent.atomic.AtomicBoolean; /** @@ -72,8 +71,6 @@ public class SocketChannelFrameHandlerState { final FrameBuilder frameBuilder; - private final AtomicBoolean isUnderflowHandlingEnabled = new AtomicBoolean(false); - public SocketChannelFrameHandlerState(SocketChannel channel, NioLoopContext nioLoopsState, NioParams nioParams, SSLEngine sslEngine) { this.channel = channel; this.readSelectorState = nioLoopsState.readSelectorState; @@ -109,7 +106,7 @@ public SocketChannelFrameHandlerState(SocketChannel channel, NioLoopContext nioL this.outputStream = new DataOutputStream( new SslEngineByteBufferOutputStream(sslEngine, plainOut, cipherOut, channel) ); - this.frameBuilder = new SslEngineFrameBuilder(sslEngine, plainIn, cipherIn, channel, isUnderflowHandlingEnabled); + this.frameBuilder = new SslEngineFrameBuilder(sslEngine, plainIn, cipherIn, channel); } } @@ -180,7 +177,7 @@ void endWriteSequence() { void prepareForReadSequence() throws IOException { if(ssl) { - if (!isUnderflowHandlingEnabled.get()) { + if (!frameBuilder.isUnderflowHandlingEnabled()) { cipherIn.clear(); cipherIn.flip(); } @@ -196,7 +193,7 @@ void prepareForReadSequence() throws IOException { boolean continueReading() throws IOException { if(ssl) { - if (isUnderflowHandlingEnabled.get()) { + if (frameBuilder.isUnderflowHandlingEnabled()) { int bytesRead = NioHelper.read(channel, cipherIn); if (bytesRead == 0) { return false; diff --git a/src/main/java/com/rabbitmq/client/impl/nio/SslEngineFrameBuilder.java b/src/main/java/com/rabbitmq/client/impl/nio/SslEngineFrameBuilder.java index 34295fb36a..8b6ecaf8ab 100644 --- a/src/main/java/com/rabbitmq/client/impl/nio/SslEngineFrameBuilder.java +++ b/src/main/java/com/rabbitmq/client/impl/nio/SslEngineFrameBuilder.java @@ -21,7 +21,6 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ReadableByteChannel; -import java.util.concurrent.atomic.AtomicBoolean; /** @@ -34,18 +33,17 @@ public class SslEngineFrameBuilder extends FrameBuilder { private final ByteBuffer cipherBuffer; - private final AtomicBoolean isUnderflowHandlingEnabled; + private boolean isUnderflowHandlingEnabled = false; - public SslEngineFrameBuilder(SSLEngine sslEngine, ByteBuffer plainIn, ByteBuffer cipherIn, ReadableByteChannel channel, final AtomicBoolean isUnderflowHandlingEnabled) { + public SslEngineFrameBuilder(SSLEngine sslEngine, ByteBuffer plainIn, ByteBuffer cipherIn, ReadableByteChannel channel) { super(channel, plainIn); this.sslEngine = sslEngine; this.cipherBuffer = cipherIn; - this.isUnderflowHandlingEnabled = isUnderflowHandlingEnabled; } @Override protected boolean somethingToRead() throws IOException { - if (applicationBuffer.hasRemaining() && !isUnderflowHandlingEnabled.get()) { + if (applicationBuffer.hasRemaining() && !isUnderflowHandlingEnabled) { return true; } else { applicationBuffer.clear(); @@ -74,11 +72,15 @@ protected boolean somethingToRead() throws IOException { throw new IllegalStateException("Invalid SSL status: " + result.getStatus()); } } finally { - isUnderflowHandlingEnabled.set(underflowHandling); + isUnderflowHandlingEnabled = underflowHandling; } return false; } } + @Override + public boolean isUnderflowHandlingEnabled() { + return isUnderflowHandlingEnabled; + } }