[Git][java-team/jboss-xnio][master] 4 commits: New upstream version 3.8.0

Markus Koschany gitlab at salsa.debian.org
Fri Mar 27 23:26:31 GMT 2020



Markus Koschany pushed to branch master at Debian Java Maintainers / jboss-xnio


Commits:
ac81ad05 by Markus Koschany at 2020-03-28T00:14:08+01:00
New upstream version 3.8.0
- - - - -
e23285d6 by Markus Koschany at 2020-03-28T00:14:13+01:00
Update upstream source from tag 'upstream/3.8.0'

Update to upstream version '3.8.0'
with Debian dir 7adf389be4bc4c14b8fcd1d768e170780ffefc89
- - - - -
c3f42a78 by Markus Koschany at 2020-03-28T00:15:09+01:00
Declare compliance with Debian Policy 4.5.0.

- - - - -
afe9826d by Markus Koschany at 2020-03-28T00:15:24+01:00
Update changelog

- - - - -


21 changed files:

- api/pom.xml
- api/src/main/java/org/xnio/ByteBufferSlicePool.java
- api/src/main/java/org/xnio/_private/Messages.java
- api/src/main/java/org/xnio/channels/Channels.java
- api/src/main/java/org/xnio/ssl/JsseSslConduitEngine.java
- api/src/main/java/org/xnio/ssl/JsseXnioSsl.java
- + api/src/test/java/org/xnio/channels/ChannelsBlockingFlushTestCase.java
- + api/src/test/java/org/xnio/channels/ChannelsBlockingTimeoutTimeoutTestCase.java
- debian/changelog
- debian/control
- nio-impl/pom.xml
- nio-impl/src/main/java/org/xnio/nio/NioXnio.java
- nio-impl/src/main/java/org/xnio/nio/NioXnioWorker.java
- − nio-impl/src/main/java/org/xnio/nio/QueuedNioTcpServer.java
- nio-impl/src/main/java/org/xnio/nio/QueuedNioTcpServerHandle.java → nio-impl/src/test/java/org/xnio/nio/test/AbstractNioSslBufferExpansionTcpTest.java
- nio-impl/src/test/java/org/xnio/nio/test/AbstractNioTcpTest.java
- + nio-impl/src/test/java/org/xnio/nio/test/NioSslBufferExpansionTcpChannelTestCase.java
- + nio-impl/src/test/java/org/xnio/nio/test/NioSslBufferExpansionTcpConnectionTestCase.java
- + nio-impl/src/test/java/org/xnio/nio/test/NioSslRandomlyTimedBufferExpansionTcpChannelTestCase.java
- + nio-impl/src/test/java/org/xnio/nio/test/NioSslRandomlyTimedBufferExpansionTcpConnectionTestCase.java
- pom.xml


Changes:

=====================================
api/pom.xml
=====================================
@@ -37,7 +37,7 @@
     <parent>
         <groupId>org.jboss.xnio</groupId>
         <artifactId>xnio-all</artifactId>
-        <version>3.7.7.Final</version>
+        <version>3.8.0.Final</version>
     </parent>
 
     <dependencies>


=====================================
api/src/main/java/org/xnio/ByteBufferSlicePool.java
=====================================
@@ -155,15 +155,11 @@ public final class ByteBufferSlicePool implements Pool<ByteBuffer> {
         // only true if using direct allocation
         if (directBuffers != null) {
             ByteBuffer region = FREE_DIRECT_BUFFERS.poll();
-            try {
-                if (region != null) {
-                    return sliceReusedBuffer(region, buffersPerRegion, bufferSize);
-                }
-                region = allocator.allocate(buffersPerRegion * bufferSize);
-                return sliceAllocatedBuffer(region, buffersPerRegion, bufferSize);
-            } finally {
-                directBuffers.add(region);
+            if (region != null) {
+                return sliceReusedBuffer(region, buffersPerRegion, bufferSize);
             }
+            region = allocator.allocate(buffersPerRegion * bufferSize);
+            return sliceAllocatedBuffer(region, buffersPerRegion, bufferSize);
         }
         return sliceAllocatedBuffer(
                 allocator.allocate(buffersPerRegion * bufferSize),


=====================================
api/src/main/java/org/xnio/_private/Messages.java
=====================================
@@ -240,6 +240,10 @@ public interface Messages extends BasicLogger {
     @Message(id = 306, value = "SSL connection is not from this provider")
     IllegalArgumentException notFromThisProvider();
 
+    @Message(id = 307, value = "Failed to close ssl engine when handling exception %s")
+    @LogMessage(level = WARN)
+    void failedToCloseSSLEngine(@Cause Throwable cause, Exception originalException);
+
     // I/O errors
 
     @Message(id = 800, value = "Read timed out")
@@ -353,4 +357,8 @@ public interface Messages extends BasicLogger {
     @Message(value = "Shutting down reads on %s failed")
     @LogMessage(level = TRACE)
     void resourceReadShutdownFailed(@Cause Throwable cause, Object resource);
+
+    @Message(value = "Expanded buffer enabled due to overflow with empty buffer, expanded buffer size is %s")
+    @LogMessage(level = TRACE)
+    void expandedSslBufferEnabled(int bufferSize);
 }


=====================================
api/src/main/java/org/xnio/channels/Channels.java
=====================================
@@ -65,6 +65,39 @@ public final class Channels {
         }
     }
 
+    /**
+     * Simple utility method to execute a blocking flush on a writable channel. The method blocks until there are no
+     * remaining bytes in the send queue or the timeout is reached.
+     *
+     * @param channel the writable channel
+     * @param time the amount of time to wait
+     * @param unit the unit of time to wait
+     * @return true if the channel was successfully flushed, false if the timeout was reached
+     * @throws IOException if an I/O exception occurs
+     *
+     * @since 3.8
+     */
+    public static boolean flushBlocking(SuspendableWriteChannel channel, long time, TimeUnit unit) throws IOException {
+        // In the fast path, the timeout is not used because bytes can be flushed without blocking.
+        if (channel.flush()) {
+            return true;
+        }
+        long remaining = unit.toNanos(time);
+        long now = System.nanoTime();
+        do {
+            // awaitWritable may return spuriously so looping is required.
+            channel.awaitWritable(remaining, TimeUnit.NANOSECONDS);
+            // flush prior to recalculating remaining time to avoid a nanoTime
+            // invocation in the optimal path.
+            if (channel.flush()) {
+                return true;
+            }
+            // Nanotime must only be used in comparison with another nanotime value
+            // This implementation allows us to avoid immediate subsequent nanoTime calls
+        } while ((remaining -= Math.max(-now + (now = System.nanoTime()), 0L)) > 0L);
+        return false;
+    }
+
     /**
      * Simple utility method to execute a blocking write shutdown on a writable channel.  The method blocks until the
      * channel's output side is fully shut down.
@@ -79,6 +112,23 @@ public final class Channels {
         flushBlocking(channel);
     }
 
+    /**
+     * Simple utility method to execute a blocking write shutdown on a writable channel.  The method blocks until the
+     * channel's output side is fully shut down or the timeout is reached.
+     *
+     * @param channel the writable channel
+     * @param time the amount of time to wait
+     * @param unit the unit of time to wait
+     * @return true if the channel was successfully flushed, false if the timeout was reached
+     * @throws IOException if an I/O exception occurs
+     *
+     * @since 3.8
+     */
+    public static boolean shutdownWritesBlocking(SuspendableWriteChannel channel, long time, TimeUnit unit) throws IOException {
+        channel.shutdownWrites();
+        return flushBlocking(channel, time, unit);
+    }
+
     /**
      * Simple utility method to execute a blocking write on a byte channel.  The method blocks until the bytes in the
      * buffer have been fully written.  To ensure that the data is sent, the {@link #flushBlocking(SuspendableWriteChannel)}
@@ -311,13 +361,27 @@ public final class Channels {
      * @since 1.2
      */
     public static <C extends ReadableByteChannel & SuspendableReadChannel> int readBlocking(C channel, ByteBuffer buffer, long time, TimeUnit unit) throws IOException {
+        // In the fast path, the timeout is not used because bytes can be read without blocking.
         int res = channel.read(buffer);
-        if (res == 0 && buffer.hasRemaining()) {
-            channel.awaitReadable(time, unit);
-            return channel.read(buffer);
-        } else {
+        if (res != 0) {
             return res;
         }
+        long remaining = unit.toNanos(time);
+        long now = System.nanoTime();
+        while (buffer.hasRemaining() && remaining > 0) {
+            // awaitReadable may return spuriously, so looping is required.
+            channel.awaitReadable(remaining, TimeUnit.NANOSECONDS);
+            // read prior to recalculating remaining time to avoid a nanoTime
+            // invocation in the optimal path.
+            res = channel.read(buffer);
+            if (res != 0) {
+                return res;
+            }
+            // Nanotime must only be used in comparison with another nanotime value
+            // This implementation allows us to avoid immediate subsequent nanoTime calls
+            remaining -= Math.max(-now + (now = System.nanoTime()), 0L);
+        }
+        return res;
     }
 
     /**
@@ -357,13 +421,28 @@ public final class Channels {
      * @since 1.2
      */
     public static <C extends ScatteringByteChannel & SuspendableReadChannel> long readBlocking(C channel, ByteBuffer[] buffers, int offs, int len, long time, TimeUnit unit) throws IOException {
+        // In the fast path, the timeout is not used because bytes can be read
+        // without blocking or interaction with the precise clock.
         long res = channel.read(buffers, offs, len);
-        if (res == 0L && Buffers.hasRemaining(buffers, offs, len)) {
-            channel.awaitReadable(time, unit);
-            return channel.read(buffers, offs, len);
-        } else {
+        if (res != 0L) {
             return res;
         }
+        long remaining = unit.toNanos(time);
+        long now = System.nanoTime();
+        while (Buffers.hasRemaining(buffers, offs, len) && remaining > 0) {
+            // awaitReadable may return spuriously, looping is required.
+            channel.awaitReadable(remaining, TimeUnit.NANOSECONDS);
+            // read prior to recalculating remaining time to avoid a nanoTime
+            // invocation in the optimal path.
+            res = channel.read(buffers, offs, len);
+            if (res != 0) {
+                return res;
+            }
+            // Nanotime must only be used in comparison with another nanotime value
+            // This implementation allows us to avoid immediate subsequent nanoTime calls
+            remaining -= Math.max(-now + (now = System.nanoTime()), 0L);
+        }
+        return res;
     }
 
     /**
@@ -399,13 +478,27 @@ public final class Channels {
      * @since 1.2
      */
     public static <C extends ReadableMessageChannel> int receiveBlocking(C channel, ByteBuffer buffer, long time, TimeUnit unit) throws IOException {
+        // In the fast path, the timeout is not used because bytes can be read without blocking.
         int res = channel.receive(buffer);
-        if ((res) == 0) {
-            channel.awaitReadable(time, unit);
-            return channel.receive(buffer);
-        } else {
+        if (res != 0) {
             return res;
         }
+        long remaining = unit.toNanos(time);
+        long now = System.nanoTime();
+        while (buffer.hasRemaining() && remaining > 0) {
+            // awaitReadable may return spuriously, looping is required.
+            channel.awaitReadable(remaining, TimeUnit.NANOSECONDS);
+            // read prior to recalculating remaining time to avoid a nanoTime
+            // invocation in the optimal path.
+            res = channel.receive(buffer);
+            if (res != 0) {
+                return res;
+            }
+            // Nanotime must only be used in comparison with another nanotime value
+            // This implementation allows us to avoid immediate subsequent nanoTime calls
+            remaining -= Math.max(-now + (now = System.nanoTime()), 0L);
+        }
+        return res;
     }
 
     /**
@@ -445,13 +538,25 @@ public final class Channels {
      * @since 1.2
      */
     public static <C extends ReadableMessageChannel> long receiveBlocking(C channel, ByteBuffer[] buffers, int offs, int len, long time, TimeUnit unit) throws IOException {
+        // In the fast path, the timeout is not used because bytes can be read without blocking.
         long res = channel.receive(buffers, offs, len);
-        if ((res) == 0) {
-            channel.awaitReadable(time, unit);
-            return channel.receive(buffers, offs, len);
-        } else {
+        if (res != 0L) {
             return res;
         }
+        long remaining = unit.toNanos(time);
+        long now = System.nanoTime();
+        while (Buffers.hasRemaining(buffers, offs, len) && remaining > 0) {
+            // awaitReadable may return spuriously, looping is required.
+            channel.awaitReadable(remaining, TimeUnit.NANOSECONDS);
+            res = channel.receive(buffers, offs, len);
+            if (res != 0) {
+                return res;
+            }
+            // Nanotime must only be used in comparison with another nanotime value
+            // This implementation allows us to avoid immediate subsequent nanoTime calls
+            remaining -= Math.max(-now + (now = System.nanoTime()), 0L);
+        }
+        return res;
     }
 
     /**


=====================================
api/src/main/java/org/xnio/ssl/JsseSslConduitEngine.java
=====================================
@@ -44,7 +44,10 @@ import javax.net.ssl.SSLHandshakeException;
 import javax.net.ssl.SSLSession;
 
 import org.jboss.logging.Logger;
+
+import org.xnio.BufferAllocator;
 import org.xnio.Buffers;
+import org.xnio.ByteBufferSlicePool;
 import org.xnio.Pool;
 import org.xnio.Pooled;
 import org.xnio.conduits.StreamSinkConduit;
@@ -86,6 +89,10 @@ final class JsseSslConduitEngine {
     private final Pooled<ByteBuffer> receiveBuffer;
     /** The buffer from which outbound SSL data is sent. */
     private final Pooled<ByteBuffer> sendBuffer;
+    /** An expanded non-final buffer from which outbound SSL data is sent when
+     * large fragments handling is enabled in the underlying SSL Engine. When
+     * this happens, we need a specific buffer with expanded capacity. */
+    private  ByteBuffer expandedSendBuffer;
     /** The buffer into which inbound clear data is written. */
     private final Pooled<ByteBuffer> readBuffer;
 
@@ -149,18 +156,11 @@ final class JsseSslConduitEngine {
             sendBuffer = socketBufferPool.allocate();
             try {
                 if (receiveBuffer.getResource().capacity() < packetBufferSize || sendBuffer.getResource().capacity() < packetBufferSize) {
-                    throw msg.socketBufferTooSmall();
+                    // create expanded send buffer
+                    expandedSendBuffer = ByteBuffer.allocate(packetBufferSize);
                 }
-                final int applicationBufferSize = session.getApplicationBufferSize();
                 readBuffer = applicationBufferPool.allocate();
-                try {
-                    if (readBuffer.getResource().capacity() < applicationBufferSize) {
-                        throw msg.appBufferTooSmall();
-                    }
-                    ok = true;
-                } finally {
-                    if (! ok) readBuffer.free();
-                }
+                ok = true;
             } finally {
                 if (! ok) sendBuffer.free();
             }
@@ -231,14 +231,13 @@ final class JsseSslConduitEngine {
             // a workaround for a bug found in SSLEngine
             throw new ClosedChannelException();
         }
-        final ByteBuffer buffer = sendBuffer.getResource();
         long bytesConsumed = 0;
         boolean run;
         try {
             do {
                 final SSLEngineResult result;
                 synchronized (getWrapLock()) {
-                    run = handleWrapResult(result = engineWrap(srcs, offset, length, buffer), false);
+                    run = handleWrapResult(result = engineWrap(srcs, offset, length, getSendBuffer()), false);
                     bytesConsumed += (long) result.bytesConsumed();
                 }
                 // handshake will tell us whether to keep the loop
@@ -247,7 +246,7 @@ final class JsseSslConduitEngine {
         } catch (SSLHandshakeException e) {
             try {
                 synchronized (getWrapLock()) {
-                    engine.wrap(EMPTY_BUFFER, sendBuffer.getResource());
+                    engine.wrap(EMPTY_BUFFER, getSendBuffer());
                     doFlush();
                 }
             } catch (IOException ignore) {}
@@ -266,7 +265,7 @@ final class JsseSslConduitEngine {
     public ByteBuffer getWrappedBuffer() {
         assert Thread.holdsLock(getWrapLock());
         assert ! Thread.holdsLock(getUnwrapLock());
-        return allAreSet(stateUpdater.get(this), ENGINE_CLOSED)? Buffers.EMPTY_BYTE_BUFFER: sendBuffer.getResource();
+        return allAreSet(stateUpdater.get(this), ENGINE_CLOSED)? Buffers.EMPTY_BYTE_BUFFER: getSendBuffer();
     }
 
     /**
@@ -300,14 +299,13 @@ final class JsseSslConduitEngine {
             throw new ClosedChannelException();
         }
         clearFlags(FIRST_HANDSHAKE);
-        final ByteBuffer buffer = sendBuffer.getResource();
         int bytesConsumed = 0;
         boolean run;
         try {
             do {
                 final SSLEngineResult result;
                 synchronized (getWrapLock()) {
-                    run = handleWrapResult(result = engineWrap(src, buffer), isCloseExpected);
+                    run = handleWrapResult(result = engineWrap(src, getSendBuffer()), isCloseExpected);
                     bytesConsumed += result.bytesConsumed();
                 }
                 // handshake will tell us whether to keep the loop
@@ -316,7 +314,7 @@ final class JsseSslConduitEngine {
         } catch (SSLHandshakeException e) {
             try {
                 synchronized (getWrapLock()) {
-                    engine.wrap(EMPTY_BUFFER, sendBuffer.getResource());
+                    engine.wrap(EMPTY_BUFFER, getSendBuffer());
                     doFlush();
                 }
             } catch (IOException ignore) {}
@@ -376,9 +374,13 @@ final class JsseSslConduitEngine {
             case BUFFER_OVERFLOW: {
                 assert result.bytesConsumed() == 0;
                 assert result.bytesProduced() == 0;
-                final ByteBuffer buffer = sendBuffer.getResource();
+                final ByteBuffer buffer = getSendBuffer();
                 if (buffer.position() == 0) {
-                    throw msg.wrongBufferExpansion();
+                    final int bufferSize = engine.getSession().getPacketBufferSize();
+                    if (buffer.capacity() < bufferSize) {
+                        msg.expandedSslBufferEnabled(bufferSize);
+                        expandedSendBuffer = ByteBuffer.allocate(bufferSize);
+                    } else throw msg.wrongBufferExpansion();
                 } else {
                     // there's some data in there, so send it first
                     buffer.flip();
@@ -462,7 +464,7 @@ final class JsseSslConduitEngine {
                     if (write) {
                         return true;
                     }
-                    final ByteBuffer buffer = sendBuffer.getResource();
+                    final ByteBuffer buffer = getSendBuffer();
                     // else, trigger a write call
                     // Needs wrap, so we wrap (if possible)...
                     synchronized (getWrapLock()) {
@@ -578,6 +580,8 @@ final class JsseSslConduitEngine {
         return (int) unwrap(new ByteBuffer[]{dst}, 0, 1);
     }
 
+    private int failureCount = 0;
+
     /**
      * Unwraps the bytes contained in {@link #getUnwrapBuffer()}, copying the resulting unwrapped bytes into
      * {@code dsts}.
@@ -632,7 +636,7 @@ final class JsseSslConduitEngine {
         } catch (SSLHandshakeException e) {
             try {
                 synchronized (getWrapLock()) {
-                    engine.wrap(EMPTY_BUFFER, sendBuffer.getResource());
+                    engine.wrap(EMPTY_BUFFER, getSendBuffer());
                     doFlush();
                 }
             } catch (IOException ignore) {}
@@ -827,15 +831,14 @@ final class JsseSslConduitEngine {
         if (sinkConduit.isWriteShutdown()) {
             return true;
         }
-        final ByteBuffer buffer = sendBuffer.getResource();
         if (!engine.isOutboundDone() || !engine.isInboundDone()) {
             SSLEngineResult result;
             do {
-                if (!handleWrapResult(result = engineWrap(Buffers.EMPTY_BYTE_BUFFER, buffer), true)) {
+                if (!handleWrapResult(result = engineWrap(Buffers.EMPTY_BYTE_BUFFER, getSendBuffer()), true)) {
                     return false;
                 }
             } while (handleHandshake(result, true) && (result.getHandshakeStatus() != HandshakeStatus.NEED_UNWRAP || !engine.isOutboundDone()));
-            handleWrapResult(result = engineWrap(Buffers.EMPTY_BYTE_BUFFER, buffer), true);
+            handleWrapResult(result = engineWrap(Buffers.EMPTY_BYTE_BUFFER, getSendBuffer()), true);
             if (!engine.isOutboundDone() || (result.getHandshakeStatus() != HandshakeStatus.NOT_HANDSHAKING &&
                     result.getHandshakeStatus() != HandshakeStatus.NEED_UNWRAP)) {
                 return false;
@@ -855,7 +858,7 @@ final class JsseSslConduitEngine {
         assert Thread.holdsLock(getWrapLock());
         assert ! Thread.holdsLock(getUnwrapLock());
         final ByteBuffer buffer;
-        buffer = sendBuffer.getResource();
+        buffer = getSendBuffer();
         buffer.flip();
         try {
             while (buffer.hasRemaining()) {
@@ -914,9 +917,13 @@ final class JsseSslConduitEngine {
             }
         } catch (Exception e) {
             //if there is an exception on close we immediately close the engine to make sure buffers are freed
-            closeEngine();
+            try {
+                closeEngine();
+            } catch (Exception closeEngineException) {
+                msg.failedToCloseSSLEngine(e, closeEngineException);
+            }
             if(e instanceof IOException) {
-                throw (IOException)e;
+                throw (IOException) e;
             } else {
                 throw (RuntimeException)e;
             }
@@ -1197,4 +1204,8 @@ final class JsseSslConduitEngine {
             }
         }
     }
+
+    private final ByteBuffer getSendBuffer() {
+        return expandedSendBuffer != null? expandedSendBuffer : sendBuffer.getResource();
+    }
 }


=====================================
api/src/main/java/org/xnio/ssl/JsseXnioSsl.java
=====================================
@@ -159,7 +159,13 @@ public final class JsseXnioSsl extends XnioSsl {
             public void handleEvent(final StreamConnection connection) {
                 final SSLEngine sslEngine = JsseSslUtils.createSSLEngine(sslContext, optionMap, destination);
                 final boolean startTls = optionMap.get(Options.SSL_STARTTLS, false);
-                final SslConnection wrappedConnection = NEW_IMPL ? new JsseSslConnection(connection, sslEngine, bufferPool, bufferPool) : new JsseSslStreamConnection(connection, sslEngine, bufferPool, bufferPool, startTls);
+                final SslConnection wrappedConnection;
+                try {
+                    wrappedConnection = NEW_IMPL ? new JsseSslConnection(connection, sslEngine, bufferPool, bufferPool) : new JsseSslStreamConnection(connection, sslEngine, bufferPool, bufferPool, startTls);
+                } catch (RuntimeException e) {
+                    futureResult.setCancelled();
+                    throw e;
+                }
                 if (NEW_IMPL && ! startTls) {
                     try {
                         wrappedConnection.startHandshake();


=====================================
api/src/test/java/org/xnio/channels/ChannelsBlockingFlushTestCase.java
=====================================
@@ -0,0 +1,150 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2020 Red Hat, Inc. and/or its affiliates, and individual
+ * contributors as indicated by the @author tags.
+ *
+ * Licensed 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.xnio.channels;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Test;
+
+/**
+ * Test for {@link Channels} blocking flush operations with timeouts.
+ *
+ * @author Carter Kozak
+ */
+public class ChannelsBlockingFlushTestCase {
+
+    @Test
+    public void testFlushBlockingSpuriousReturn() throws IOException {
+        List<String> invocations = new ArrayList<String>();
+        SuspendableWriteChannel stubChannel = (SuspendableWriteChannel) Proxy.newProxyInstance(
+                getClass().getClassLoader(),
+                new Class<?>[]{SuspendableWriteChannel.class},
+                new InvocationHandler() {
+                    int flushes = 0;
+                    @Override
+                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                        invocations.add(method.getName());
+                        if ("flush".equals(method.getName())) {
+                            return flushes++ > 1;
+                        } else if ("awaitWritable".equals(method.getName())) {
+                            return null;
+                        }
+                        throw new IllegalStateException("Unexpected method invocation: "
+                                + method + " with args " + Arrays.toString(args));
+                    }
+                });
+        assertTrue(Channels.flushBlocking(stubChannel,1, TimeUnit.SECONDS));
+        // Validate that awaitReadable was called multiple times, and is always followed by a read.
+        assertEquals(Arrays.asList("flush", "awaitWritable", "flush", "awaitWritable", "flush"), invocations);
+    }
+
+    @Test
+    public void testFlushBlockingTimeout() throws IOException {
+        List<String> invocations = new ArrayList<String>();
+        SuspendableWriteChannel stubChannel = (SuspendableWriteChannel) Proxy.newProxyInstance(
+                getClass().getClassLoader(),
+                new Class<?>[]{SuspendableWriteChannel.class},
+                new InvocationHandler() {
+                    int flushes = 0;
+                    @Override
+                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                        invocations.add(method.getName());
+                        if ("flush".equals(method.getName())) {
+                            return flushes++ > 1;
+                        } else if ("awaitWritable".equals(method.getName())) {
+                            // Sleep twenty milliseconds, which exceeds our ten millisecond timeout
+                            Thread.sleep(20);
+                            return null;
+                        }
+                        throw new IllegalStateException("Unexpected method invocation: "
+                                + method + " with args " + Arrays.toString(args));
+                    }
+                });
+        assertFalse(Channels.flushBlocking(stubChannel,10, TimeUnit.MILLISECONDS));
+        // Validate that awaitReadable was called multiple times, and is always followed by a read.
+        assertEquals(Arrays.asList("flush", "awaitWritable", "flush"), invocations);
+    }
+
+    @Test
+    public void testShutdownWritesBlockingSpuriousReturn() throws IOException {
+        List<String> invocations = new ArrayList<String>();
+        SuspendableWriteChannel stubChannel = (SuspendableWriteChannel) Proxy.newProxyInstance(
+                getClass().getClassLoader(),
+                new Class<?>[]{SuspendableWriteChannel.class},
+                new InvocationHandler() {
+                    int flushes = 0;
+                    @Override
+                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                        invocations.add(method.getName());
+                        if ("flush".equals(method.getName())) {
+                            return flushes++ > 1;
+                        } else if ("awaitWritable".equals(method.getName())) {
+                            return null;
+                        } else if ("shutdownWrites".equals(method.getName())) {
+                            return null;
+                        }
+                        throw new IllegalStateException("Unexpected method invocation: "
+                                + method + " with args " + Arrays.toString(args));
+                    }
+                });
+        assertTrue(Channels.shutdownWritesBlocking(stubChannel,1, TimeUnit.SECONDS));
+        // Validate that awaitReadable was called multiple times, and is always followed by a read.
+        assertEquals(Arrays.asList("shutdownWrites", "flush", "awaitWritable", "flush", "awaitWritable", "flush"), invocations);
+    }
+
+    @Test
+    public void testShutdownWritesBlockingTimeout() throws IOException {
+        List<String> invocations = new ArrayList<String>();
+        SuspendableWriteChannel stubChannel = (SuspendableWriteChannel) Proxy.newProxyInstance(
+                getClass().getClassLoader(),
+                new Class<?>[]{SuspendableWriteChannel.class},
+                new InvocationHandler() {
+                    int flushes = 0;
+                    @Override
+                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                        invocations.add(method.getName());
+                        if ("flush".equals(method.getName())) {
+                            return flushes++ > 1;
+                        } else if ("awaitWritable".equals(method.getName())) {
+                            // Sleep twenty milliseconds, which exceeds our ten millisecond timeout
+                            Thread.sleep(20);
+                            return null;
+                        } else if ("shutdownWrites".equals(method.getName())) {
+                            return null;
+                        }
+                        throw new IllegalStateException("Unexpected method invocation: "
+                                + method + " with args " + Arrays.toString(args));
+                    }
+                });
+        assertFalse(Channels.shutdownWritesBlocking(stubChannel,10, TimeUnit.MILLISECONDS));
+        // Validate that awaitReadable was called multiple times, and is always followed by a read.
+        assertEquals(Arrays.asList("shutdownWrites", "flush", "awaitWritable", "flush"), invocations);
+    }
+}


=====================================
api/src/test/java/org/xnio/channels/ChannelsBlockingTimeoutTimeoutTestCase.java
=====================================
@@ -0,0 +1,284 @@
+package org.xnio.channels;
+
+import org.junit.Test;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.nio.ByteBuffer;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.ScatteringByteChannel;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test for {@link Channels} blocking operations with timeouts.
+ *
+ * @author Carter Kozak
+ */
+public class ChannelsBlockingTimeoutTimeoutTestCase {
+
+    @Test
+    public void testSingleBufferReadBlockingSpuriousReturn() throws IOException {
+        List<String> invocations = new ArrayList<String>();
+        ReadableSuspendableChannel stubChannel = (ReadableSuspendableChannel) Proxy.newProxyInstance(
+                getClass().getClassLoader(),
+                new Class<?>[]{ReadableSuspendableChannel.class},
+                new InvocationHandler() {
+                    int reads = 0;
+                    @Override
+                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                        invocations.add(method.getName());
+                        if ("read".equals(method.getName())) {
+                            if (reads++ <= 1) {
+                                return 0;
+                            } else {
+                                return 1;
+                            }
+                        } else if ("awaitReadable".equals(method.getName())) {
+                            return null;
+                        }
+                        throw new IllegalStateException("Unexpected method invocation: "
+                                + method + " with args " + Arrays.toString(args));
+                    }
+                });
+        int result = Channels.readBlocking(stubChannel, ByteBuffer.allocate(32), 1, TimeUnit.SECONDS);
+        assertEquals(1, result);
+        // Validate that awaitReadable was called multiple times, and is always followed by a read.
+        assertEquals(Arrays.asList("read", "awaitReadable", "read", "awaitReadable", "read"), invocations);
+    }
+
+    @Test
+    public void testSingleBufferReadBlockingSpuriousReturnReachesTimeout() throws IOException {
+        List<String> invocations = new ArrayList<String>();
+        ReadableSuspendableChannel stubChannel = (ReadableSuspendableChannel) Proxy.newProxyInstance(
+                getClass().getClassLoader(),
+                new Class<?>[]{ReadableSuspendableChannel.class},
+                new InvocationHandler() {
+                    int reads = 0;
+                    @Override
+                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                        invocations.add(method.getName());
+                        if ("read".equals(method.getName())) {
+                            if (reads++ <= 1) {
+                                return 0;
+                            } else {
+                                return 1;
+                            }
+                        } else if ("awaitReadable".equals(method.getName())) {
+                            // Sleep twenty milliseconds, which exceeds our ten millisecond timeout
+                            Thread.sleep(20);
+                            return null;
+                        }
+                        throw new IllegalStateException("Unexpected method invocation: "
+                                + method + " with args " + Arrays.toString(args));
+                    }
+                });
+        // Note, use ten milliseconds to avoid issues on platforms with coarse clocks
+        int result = Channels.readBlocking(stubChannel, ByteBuffer.allocate(32), 10, TimeUnit.MILLISECONDS);
+        assertEquals(0, result);
+        // Validate that awaitReadable was called multiple times, and is always followed by a read.
+        assertEquals(Arrays.asList("read", "awaitReadable", "read"), invocations);
+    }
+
+    @Test
+    public void testMultiBufferReadBlockingSpuriousReturn() throws IOException {
+        List<String> invocations = new ArrayList<String>();
+        ReadableSuspendableChannel stubChannel = (ReadableSuspendableChannel) Proxy.newProxyInstance(
+                getClass().getClassLoader(),
+                new Class<?>[]{ReadableSuspendableChannel.class},
+                new InvocationHandler() {
+                    int reads = 0;
+                    @Override
+                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                        invocations.add(method.getName());
+                        if ("read".equals(method.getName())) {
+                            if (reads++ <= 1) {
+                                return 0L;
+                            } else {
+                                return 1L;
+                            }
+                        } else if ("awaitReadable".equals(method.getName())) {
+                            return null;
+                        }
+                        throw new IllegalStateException("Unexpected method invocation: "
+                                + method + " with args " + Arrays.toString(args));
+                    }
+                });
+        long result = Channels.readBlocking(stubChannel, new ByteBuffer[] {ByteBuffer.allocate(32)}, 0, 10, 1, TimeUnit.SECONDS);
+        assertEquals(1L, result);
+        // Validate that awaitReadable was called multiple times, and is always followed by a read.
+        assertEquals(Arrays.asList("read", "awaitReadable", "read", "awaitReadable", "read"), invocations);
+    }
+
+    @Test
+    public void testMultiBufferReadBlockingSpuriousReturnReachesTimeout() throws IOException {
+        List<String> invocations = new ArrayList<String>();
+        ReadableSuspendableChannel stubChannel = (ReadableSuspendableChannel) Proxy.newProxyInstance(
+                getClass().getClassLoader(),
+                new Class<?>[]{ReadableSuspendableChannel.class},
+                new InvocationHandler() {
+                    int reads = 0;
+                    @Override
+                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                        invocations.add(method.getName());
+                        if ("read".equals(method.getName())) {
+                            if (reads++ <= 1) {
+                                return 0L;
+                            } else {
+                                return 1L;
+                            }
+                        } else if ("awaitReadable".equals(method.getName())) {
+                            // Sleep twenty milliseconds, which exceeds our ten millisecond timeout
+                            Thread.sleep(20);
+                            return null;
+                        }
+                        throw new IllegalStateException("Unexpected method invocation: "
+                                + method + " with args " + Arrays.toString(args));
+                    }
+                });
+        // Note, use ten milliseconds to avoid issues on platforms with coarse clocks
+        long result = Channels.readBlocking(stubChannel, new ByteBuffer[] {ByteBuffer.allocate(32)}, 0, 10, 10, TimeUnit.MILLISECONDS);
+        assertEquals(0L, result);
+        // Validate that awaitReadable was called multiple times, and is always followed by a read.
+        assertEquals(Arrays.asList("read", "awaitReadable", "read"), invocations);
+    }
+
+    // here
+
+    @Test
+    public void testSingleBufferReceiveBlockingSpuriousReturn() throws IOException {
+        List<String> invocations = new ArrayList<String>();
+        ReadableSuspendableChannel stubChannel = (ReadableSuspendableChannel) Proxy.newProxyInstance(
+                getClass().getClassLoader(),
+                new Class<?>[]{ReadableSuspendableChannel.class},
+                new InvocationHandler() {
+                    int reads = 0;
+                    @Override
+                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                        invocations.add(method.getName());
+                        if ("receive".equals(method.getName())) {
+                            if (reads++ <= 1) {
+                                return 0;
+                            } else {
+                                return 1;
+                            }
+                        } else if ("awaitReadable".equals(method.getName())) {
+                            return null;
+                        }
+                        throw new IllegalStateException("Unexpected method invocation: "
+                                + method + " with args " + Arrays.toString(args));
+                    }
+                });
+        int result = Channels.receiveBlocking(stubChannel, ByteBuffer.allocate(32), 1, TimeUnit.SECONDS);
+        assertEquals(1, result);
+        // Validate that awaitReadable was called multiple times, and is always followed by a read.
+        assertEquals(Arrays.asList("receive", "awaitReadable", "receive", "awaitReadable", "receive"), invocations);
+    }
+
+    @Test
+    public void testSingleBufferReceiveBlockingSpuriousReturnReachesTimeout() throws IOException {
+        List<String> invocations = new ArrayList<String>();
+        ReadableSuspendableChannel stubChannel = (ReadableSuspendableChannel) Proxy.newProxyInstance(
+                getClass().getClassLoader(),
+                new Class<?>[]{ReadableSuspendableChannel.class},
+                new InvocationHandler() {
+                    int reads = 0;
+                    @Override
+                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                        invocations.add(method.getName());
+                        if ("receive".equals(method.getName())) {
+                            if (reads++ <= 1) {
+                                return 0;
+                            } else {
+                                return 1;
+                            }
+                        } else if ("awaitReadable".equals(method.getName())) {
+                            // Sleep twenty milliseconds, which exceeds our ten millisecond timeout
+                            Thread.sleep(20);
+                            return null;
+                        }
+                        throw new IllegalStateException("Unexpected method invocation: "
+                                + method + " with args " + Arrays.toString(args));
+                    }
+                });
+        // Note, use ten milliseconds to avoid issues on platforms with coarse clocks
+        int result = Channels.receiveBlocking(stubChannel, ByteBuffer.allocate(32), 10, TimeUnit.MILLISECONDS);
+        assertEquals(0, result);
+        // Validate that awaitReadable was called multiple times, and is always followed by a read.
+        assertEquals(Arrays.asList("receive", "awaitReadable", "receive"), invocations);
+    }
+
+    @Test
+    public void testMultiBufferReceiveBlockingSpuriousReturn() throws IOException {
+        List<String> invocations = new ArrayList<String>();
+        ReadableSuspendableChannel stubChannel = (ReadableSuspendableChannel) Proxy.newProxyInstance(
+                getClass().getClassLoader(),
+                new Class<?>[]{ReadableSuspendableChannel.class},
+                new InvocationHandler() {
+                    int reads = 0;
+                    @Override
+                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                        invocations.add(method.getName());
+                        if ("receive".equals(method.getName())) {
+                            if (reads++ <= 1) {
+                                return 0L;
+                            } else {
+                                return 1L;
+                            }
+                        } else if ("awaitReadable".equals(method.getName())) {
+                            return null;
+                        }
+                        throw new IllegalStateException("Unexpected method invocation: "
+                                + method + " with args " + Arrays.toString(args));
+                    }
+                });
+        long result = Channels.receiveBlocking(stubChannel, new ByteBuffer[] {ByteBuffer.allocate(32)}, 0, 10, 1, TimeUnit.SECONDS);
+        assertEquals(1L, result);
+        // Validate that awaitReadable was called multiple times, and is always followed by a read.
+        assertEquals(Arrays.asList("receive", "awaitReadable", "receive", "awaitReadable", "receive"), invocations);
+    }
+
+    @Test
+    public void testMultiBufferReceiveBlockingSpuriousReturnReachesTimeout() throws IOException {
+        List<String> invocations = new ArrayList<String>();
+        ReadableSuspendableChannel stubChannel = (ReadableSuspendableChannel) Proxy.newProxyInstance(
+                getClass().getClassLoader(),
+                new Class<?>[]{ReadableSuspendableChannel.class},
+                new InvocationHandler() {
+                    int reads = 0;
+                    @Override
+                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                        invocations.add(method.getName());
+                        if ("receive".equals(method.getName())) {
+                            if (reads++ <= 1) {
+                                return 0L;
+                            } else {
+                                return 1L;
+                            }
+                        } else if ("awaitReadable".equals(method.getName())) {
+                            // Sleep twenty milliseconds, which exceeds our ten millisecond timeout
+                            Thread.sleep(20);
+                            return null;
+                        }
+                        throw new IllegalStateException("Unexpected method invocation: "
+                                + method + " with args " + Arrays.toString(args));
+                    }
+                });
+        // Note, use ten milliseconds to avoid issues on platforms with coarse clocks
+        long result = Channels.receiveBlocking(stubChannel, new ByteBuffer[] {ByteBuffer.allocate(32)}, 0, 10, 10, TimeUnit.MILLISECONDS);
+        assertEquals(0L, result);
+        // Validate that awaitReadable was called multiple times, and is always followed by a read.
+        assertEquals(Arrays.asList("receive", "awaitReadable", "receive"), invocations);
+    }
+
+    interface ReadableSuspendableChannel
+            extends ReadableByteChannel, SuspendableReadChannel, ScatteringByteChannel, ReadableMessageChannel {
+
+    }
+}


=====================================
debian/changelog
=====================================
@@ -1,3 +1,10 @@
+jboss-xnio (3.8.0-1) unstable; urgency=medium
+
+  * New upstream version 3.8.0.
+  * Declare compliance with Debian Policy 4.5.0.
+
+ -- Markus Koschany <apo at debian.org>  Sat, 28 Mar 2020 00:15:15 +0100
+
 jboss-xnio (3.7.7-1) unstable; urgency=medium
 
   * New upstream version 3.7.7.


=====================================
debian/control
=====================================
@@ -20,7 +20,7 @@ Build-Depends:
  libwildfly-client-config-java,
  libwildfly-common-java,
  maven-debian-helper (>= 1.5)
-Standards-Version: 4.4.1
+Standards-Version: 4.5.0
 Vcs-Git: https://salsa.debian.org/java-team/jboss-xnio.git
 Vcs-Browser: https://salsa.debian.org/java-team/jboss-xnio
 Homepage: http://xnio.jboss.org/


=====================================
nio-impl/pom.xml
=====================================
@@ -31,7 +31,7 @@
     <parent>
         <groupId>org.jboss.xnio</groupId>
         <artifactId>xnio-all</artifactId>
-        <version>3.7.7.Final</version>
+        <version>3.8.0.Final</version>
     </parent>
     
     <properties>
@@ -164,6 +164,7 @@
                     <enableAssertions>true</enableAssertions>
                     <redirectTestOutputToFile>true</redirectTestOutputToFile>
                     <trimStackTrace>false</trimStackTrace>
+                    <reuseForks>false</reuseForks>
                 </configuration>
             </plugin>
             <plugin>


=====================================
nio-impl/src/main/java/org/xnio/nio/NioXnio.java
=====================================
@@ -47,7 +47,6 @@ final class NioXnio extends Xnio {
 
     static final boolean IS_HP_UX;
     static final boolean HAS_BUGGY_EVENT_PORT;
-    static final boolean USE_ALT_QUEUED_SERVER;
 
     interface SelectorCreator {
         Selector open() throws IOException;
@@ -65,7 +64,6 @@ final class NioXnio extends Xnio {
                 return Boolean.valueOf(System.getProperty("os.name", "unknown").equalsIgnoreCase("hp-ux"));
             }
         }).booleanValue();
-        USE_ALT_QUEUED_SERVER = Boolean.parseBoolean(AccessController.doPrivileged(new ReadPropertyAction("xnio.nio.alt-queued-server", "true")));
         // if a JDK is released with a fix, we can try to detect it and set this to "false" for those JDKs.
         HAS_BUGGY_EVENT_PORT = true;
     }


=====================================
nio-impl/src/main/java/org/xnio/nio/NioXnioWorker.java
=====================================
@@ -184,13 +184,8 @@ final class NioXnioWorker extends XnioWorker {
                 server.setAcceptListener(acceptListener);
                 ok = true;
                 return server;
-            } else if (NioXnio.USE_ALT_QUEUED_SERVER) {
-                final QueuedNioTcpServer2 server = new QueuedNioTcpServer2(new NioTcpServer(this, channel, optionMap, true));
-                server.setAcceptListener(acceptListener);
-                ok = true;
-                return server;
             } else {
-                final QueuedNioTcpServer server = new QueuedNioTcpServer(this, channel, optionMap);
+                final QueuedNioTcpServer2 server = new QueuedNioTcpServer2(new NioTcpServer(this, channel, optionMap, true));
                 server.setAcceptListener(acceptListener);
                 ok = true;
                 return server;


=====================================
nio-impl/src/main/java/org/xnio/nio/QueuedNioTcpServer.java deleted
=====================================
@@ -1,535 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source
- *
- * Copyright 2015 Red Hat, Inc. and/or its affiliates.
- *
- * Licensed 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.xnio.nio;
-
-import static org.xnio.IoUtils.safeClose;
-import static org.xnio.nio.Log.log;
-import static org.xnio.nio.Log.tcpServerConnectionLimitLog;
-import static org.xnio.nio.Log.tcpServerLog;
-
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.net.SocketAddress;
-import java.nio.channels.ClosedChannelException;
-import java.nio.channels.SelectionKey;
-import java.nio.channels.ServerSocketChannel;
-import java.nio.channels.SocketChannel;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.ThreadLocalRandom;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
-import java.util.concurrent.atomic.AtomicLongFieldUpdater;
-
-import org.jboss.logging.Logger;
-import org.xnio.ChannelListener;
-import org.xnio.ChannelListeners;
-import org.xnio.ManagementRegistration;
-import org.xnio.IoUtils;
-import org.xnio.LocalSocketAddress;
-import org.xnio.Option;
-import org.xnio.OptionMap;
-import org.xnio.Options;
-import org.xnio.StreamConnection;
-import org.xnio.XnioExecutor;
-import org.xnio.channels.AcceptListenerSettable;
-import org.xnio.channels.AcceptingChannel;
-import org.xnio.channels.UnsupportedOptionException;
-import org.xnio.management.XnioServerMXBean;
-
-final class QueuedNioTcpServer extends AbstractNioChannel<QueuedNioTcpServer> implements AcceptingChannel<StreamConnection>, AcceptListenerSettable<QueuedNioTcpServer> {
-    private static final String FQCN = QueuedNioTcpServer.class.getName();
-
-    private volatile ChannelListener<? super QueuedNioTcpServer> acceptListener;
-
-    private final QueuedNioTcpServerHandle handle;
-    private final WorkerThread thread;
-
-    private final ServerSocketChannel channel;
-    private final ServerSocket socket;
-    private final ManagementRegistration mbeanHandle;
-
-    private final List<BlockingQueue<SocketChannel>> acceptQueues;
-
-    private static final Set<Option<?>> options = Option.setBuilder()
-            .add(Options.REUSE_ADDRESSES)
-            .add(Options.RECEIVE_BUFFER)
-            .add(Options.SEND_BUFFER)
-            .add(Options.KEEP_ALIVE)
-            .add(Options.TCP_OOB_INLINE)
-            .add(Options.TCP_NODELAY)
-            .add(Options.CONNECTION_HIGH_WATER)
-            .add(Options.CONNECTION_LOW_WATER)
-            .add(Options.READ_TIMEOUT)
-            .add(Options.WRITE_TIMEOUT)
-            .create();
-
-    @SuppressWarnings("unused")
-    private volatile int keepAlive;
-    @SuppressWarnings("unused")
-    private volatile int oobInline;
-    @SuppressWarnings("unused")
-    private volatile int tcpNoDelay;
-    @SuppressWarnings("unused")
-    private volatile int sendBuffer = -1;
-    @SuppressWarnings("unused")
-    private volatile long connectionStatus = CONN_LOW_MASK | CONN_HIGH_MASK;
-    @SuppressWarnings("unused")
-    private volatile int readTimeout;
-    @SuppressWarnings("unused")
-    private volatile int writeTimeout;
-
-    private static final long CONN_LOW_MASK     = 0x000000007FFFFFFFL;
-    private static final long CONN_LOW_BIT      = 0L;
-    @SuppressWarnings("unused")
-    private static final long CONN_LOW_ONE      = 1L;
-    private static final long CONN_HIGH_MASK    = 0x3FFFFFFF80000000L;
-    private static final long CONN_HIGH_BIT     = 31L;
-    @SuppressWarnings("unused")
-    private static final long CONN_HIGH_ONE     = 1L << CONN_HIGH_BIT;
-
-    /**
-     * The current number of open connections, can only be accessed by the accept thread
-     */
-    private int openConnections;
-    private boolean limitwarn = true;
-    private volatile boolean suspendedDueToWatermark;
-    private volatile boolean suspended;
-
-    private static final AtomicIntegerFieldUpdater<QueuedNioTcpServer> keepAliveUpdater = AtomicIntegerFieldUpdater.newUpdater(QueuedNioTcpServer.class, "keepAlive");
-    private static final AtomicIntegerFieldUpdater<QueuedNioTcpServer> oobInlineUpdater = AtomicIntegerFieldUpdater.newUpdater(QueuedNioTcpServer.class, "oobInline");
-    private static final AtomicIntegerFieldUpdater<QueuedNioTcpServer> tcpNoDelayUpdater = AtomicIntegerFieldUpdater.newUpdater(QueuedNioTcpServer.class, "tcpNoDelay");
-    private static final AtomicIntegerFieldUpdater<QueuedNioTcpServer> sendBufferUpdater = AtomicIntegerFieldUpdater.newUpdater(QueuedNioTcpServer.class, "sendBuffer");
-    private static final AtomicIntegerFieldUpdater<QueuedNioTcpServer> readTimeoutUpdater = AtomicIntegerFieldUpdater.newUpdater(QueuedNioTcpServer.class, "readTimeout");
-    private static final AtomicIntegerFieldUpdater<QueuedNioTcpServer> writeTimeoutUpdater = AtomicIntegerFieldUpdater.newUpdater(QueuedNioTcpServer.class, "writeTimeout");
-
-    private static final AtomicLongFieldUpdater<QueuedNioTcpServer> connectionStatusUpdater = AtomicLongFieldUpdater.newUpdater(QueuedNioTcpServer.class, "connectionStatus");
-    private final Runnable acceptTask = new Runnable() {
-        public void run() {
-            final WorkerThread current = WorkerThread.getCurrent();
-            assert current != null;
-            final BlockingQueue<SocketChannel> queue = acceptQueues.get(current.getNumber());
-            ChannelListeners.invokeChannelListener(QueuedNioTcpServer.this, getAcceptListener());
-            if (! queue.isEmpty() && !suspendedDueToWatermark) {
-                current.execute(this);
-            }
-        }
-    };
-
-    private final Runnable connectionClosedTask = new Runnable() {
-        @Override
-        public void run() {
-            openConnections--;
-            if(suspendedDueToWatermark && openConnections < getLowWater(connectionStatus)) {
-                synchronized (QueuedNioTcpServer.this) {
-                    suspendedDueToWatermark = false;
-                }
-            }
-        }
-    };
-
-    QueuedNioTcpServer(final NioXnioWorker worker, final ServerSocketChannel channel, final OptionMap optionMap) throws IOException {
-        super(worker);
-        this.channel = channel;
-        this.thread = worker.getAcceptThread();
-        final WorkerThread[] workerThreads = worker.getAll();
-        final List<BlockingQueue<SocketChannel>> acceptQueues = new ArrayList<>(workerThreads.length);
-        for (int i = 0; i < workerThreads.length; i++) {
-            acceptQueues.add(i, new LinkedBlockingQueue<SocketChannel>());
-        }
-        this.acceptQueues = acceptQueues;
-        socket = channel.socket();
-        if (optionMap.contains(Options.SEND_BUFFER)) {
-            final int sendBufferSize = optionMap.get(Options.SEND_BUFFER, DEFAULT_BUFFER_SIZE);
-            if (sendBufferSize < 1) {
-                throw log.parameterOutOfRange("sendBufferSize");
-            }
-            sendBufferUpdater.set(this, sendBufferSize);
-        }
-        if (optionMap.contains(Options.KEEP_ALIVE)) {
-            keepAliveUpdater.lazySet(this, optionMap.get(Options.KEEP_ALIVE, false) ? 1 : 0);
-        }
-        if (optionMap.contains(Options.TCP_OOB_INLINE)) {
-            oobInlineUpdater.lazySet(this, optionMap.get(Options.TCP_OOB_INLINE, false) ? 1 : 0);
-        }
-        if (optionMap.contains(Options.TCP_NODELAY)) {
-            tcpNoDelayUpdater.lazySet(this, optionMap.get(Options.TCP_NODELAY, false) ? 1 : 0);
-        }
-        if (optionMap.contains(Options.READ_TIMEOUT)) {
-            readTimeoutUpdater.lazySet(this, optionMap.get(Options.READ_TIMEOUT, 0));
-        }
-        if (optionMap.contains(Options.WRITE_TIMEOUT)) {
-            writeTimeoutUpdater.lazySet(this, optionMap.get(Options.WRITE_TIMEOUT, 0));
-        }
-        final int highWater;
-        final int lowWater;
-        if (optionMap.contains(Options.CONNECTION_HIGH_WATER) || optionMap.contains(Options.CONNECTION_LOW_WATER)) {
-            highWater = optionMap.get(Options.CONNECTION_HIGH_WATER, Integer.MAX_VALUE);
-            lowWater = optionMap.get(Options.CONNECTION_LOW_WATER, highWater);
-            if (highWater <= 0) {
-                throw badHighWater();
-            }
-            if (lowWater <= 0 || lowWater > highWater) {
-                throw badLowWater(highWater);
-            }
-            final long highLowWater = (long) highWater << CONN_HIGH_BIT | (long) lowWater << CONN_LOW_BIT;
-            connectionStatusUpdater.lazySet(this, highLowWater);
-        } else {
-            highWater = Integer.MAX_VALUE;
-            lowWater = Integer.MAX_VALUE;
-            connectionStatusUpdater.lazySet(this, CONN_LOW_MASK | CONN_HIGH_MASK);
-        }
-        final SelectionKey key = thread.registerChannel(channel);
-        handle = new QueuedNioTcpServerHandle(this, thread, key, highWater, lowWater);
-        key.attach(handle);
-        mbeanHandle = worker.registerServerMXBean(
-                new XnioServerMXBean() {
-                    public String getProviderName() {
-                        return "nio";
-                    }
-
-                    public String getWorkerName() {
-                        return worker.getName();
-                    }
-
-                    public String getBindAddress() {
-                        return String.valueOf(getLocalAddress());
-                    }
-
-                    public int getConnectionCount() {
-                        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(
-                                () -> openConnections, handle.getWorkerThread()
-                        );
-                        try {
-                            return future.get();
-                        } catch (InterruptedException | ExecutionException e) {
-                            return -1;
-                        }
-                    }
-
-                    public int getConnectionLimitHighWater() {
-                        return getHighWater(connectionStatus);
-                    }
-
-                    public int getConnectionLimitLowWater() {
-                        return getLowWater(connectionStatus);
-                    }
-                });
-    }
-
-    private static IllegalArgumentException badLowWater(final int highWater) {
-        return new IllegalArgumentException("Low water must be greater than 0 and less than or equal to high water (" + highWater + ")");
-    }
-
-    private static IllegalArgumentException badHighWater() {
-        return new IllegalArgumentException("High water must be greater than 0");
-    }
-
-    public void close() throws IOException {
-        try {
-            channel.close();
-        } finally {
-            handle.cancelKey(true);
-            safeClose(mbeanHandle);
-        }
-    }
-
-    public boolean supportsOption(final Option<?> option) {
-        return options.contains(option);
-    }
-
-    public <T> T getOption(final Option<T> option) throws UnsupportedOptionException, IOException {
-        if (option == Options.REUSE_ADDRESSES) {
-            return option.cast(Boolean.valueOf(socket.getReuseAddress()));
-        } else if (option == Options.RECEIVE_BUFFER) {
-            return option.cast(Integer.valueOf(socket.getReceiveBufferSize()));
-        } else if (option == Options.SEND_BUFFER) {
-            final int value = sendBuffer;
-            return value == -1 ? null : option.cast(Integer.valueOf(value));
-        } else if (option == Options.KEEP_ALIVE) {
-            return option.cast(Boolean.valueOf(keepAlive != 0));
-        } else if (option == Options.TCP_OOB_INLINE) {
-            return option.cast(Boolean.valueOf(oobInline != 0));
-        } else if (option == Options.TCP_NODELAY) {
-            return option.cast(Boolean.valueOf(tcpNoDelay != 0));
-        } else if (option == Options.READ_TIMEOUT) {
-            return option.cast(Integer.valueOf(readTimeout));
-        } else if (option == Options.WRITE_TIMEOUT) {
-            return option.cast(Integer.valueOf(writeTimeout));
-        } else if (option == Options.CONNECTION_HIGH_WATER) {
-            return option.cast(Integer.valueOf(getHighWater(connectionStatus)));
-        } else if (option == Options.CONNECTION_LOW_WATER) {
-            return option.cast(Integer.valueOf(getLowWater(connectionStatus)));
-        } else {
-            return null;
-        }
-    }
-
-    public <T> T setOption(final Option<T> option, final T value) throws IllegalArgumentException, IOException {
-        final Object old;
-        if (option == Options.REUSE_ADDRESSES) {
-            old = Boolean.valueOf(socket.getReuseAddress());
-            socket.setReuseAddress(Options.REUSE_ADDRESSES.cast(value, Boolean.FALSE).booleanValue());
-        } else if (option == Options.RECEIVE_BUFFER) {
-            old = Integer.valueOf(socket.getReceiveBufferSize());
-            final int newValue = Options.RECEIVE_BUFFER.cast(value, Integer.valueOf(DEFAULT_BUFFER_SIZE)).intValue();
-            if (newValue < 1) {
-                throw log.optionOutOfRange("RECEIVE_BUFFER");
-            }
-            socket.setReceiveBufferSize(newValue);
-        } else if (option == Options.SEND_BUFFER) {
-            final int newValue = Options.SEND_BUFFER.cast(value, Integer.valueOf(DEFAULT_BUFFER_SIZE)).intValue();
-            if (newValue < 1) {
-                throw log.optionOutOfRange("SEND_BUFFER");
-            }
-            final int oldValue = sendBufferUpdater.getAndSet(this, newValue);
-            old = oldValue == -1 ? null : Integer.valueOf(oldValue);
-        } else if (option == Options.KEEP_ALIVE) {
-            old = Boolean.valueOf(keepAliveUpdater.getAndSet(this, Options.KEEP_ALIVE.cast(value, Boolean.FALSE).booleanValue() ? 1 : 0) != 0);
-        } else if (option == Options.TCP_OOB_INLINE) {
-            old = Boolean.valueOf(oobInlineUpdater.getAndSet(this, Options.TCP_OOB_INLINE.cast(value, Boolean.FALSE).booleanValue() ? 1 : 0) != 0);
-        } else if (option == Options.TCP_NODELAY) {
-            old = Boolean.valueOf(tcpNoDelayUpdater.getAndSet(this, Options.TCP_NODELAY.cast(value, Boolean.FALSE).booleanValue() ? 1 : 0) != 0);
-        } else if (option == Options.READ_TIMEOUT) {
-            old = Integer.valueOf(readTimeoutUpdater.getAndSet(this, Options.READ_TIMEOUT.cast(value, Integer.valueOf(0)).intValue()));
-        } else if (option == Options.WRITE_TIMEOUT) {
-            old = Integer.valueOf(writeTimeoutUpdater.getAndSet(this, Options.WRITE_TIMEOUT.cast(value, Integer.valueOf(0)).intValue()));
-        } else if (option == Options.CONNECTION_HIGH_WATER) {
-            old = Integer.valueOf(getHighWater(updateWaterMark(-1, Options.CONNECTION_HIGH_WATER.cast(value, Integer.valueOf(Integer.MAX_VALUE)).intValue())));
-        } else if (option == Options.CONNECTION_LOW_WATER) {
-            old = Integer.valueOf(getLowWater(updateWaterMark(Options.CONNECTION_LOW_WATER.cast(value, Integer.valueOf(Integer.MAX_VALUE)).intValue(), -1)));
-        } else {
-            return null;
-        }
-        return option.cast(old);
-    }
-
-    private long updateWaterMark(int reqNewLowWater, int reqNewHighWater) {
-        // at least one must be specified
-        assert reqNewLowWater != -1 || reqNewHighWater != -1;
-        // if both given, low must be less than high
-        assert reqNewLowWater == -1 || reqNewHighWater == -1 || reqNewLowWater <= reqNewHighWater;
-
-        long oldVal, newVal;
-        int oldHighWater, oldLowWater;
-        int newLowWater, newHighWater;
-
-        do {
-            oldVal = connectionStatus;
-            oldLowWater = getLowWater(oldVal);
-            oldHighWater = getHighWater(oldVal);
-            newLowWater = reqNewLowWater == -1 ? oldLowWater : reqNewLowWater;
-            newHighWater = reqNewHighWater == -1 ? oldHighWater : reqNewHighWater;
-            // Make sure the new values make sense
-            if (reqNewLowWater != -1 && newLowWater > newHighWater) {
-                newHighWater = newLowWater;
-            } else if (reqNewHighWater != -1 && newHighWater < newLowWater) {
-                newLowWater = newHighWater;
-            }
-            // See if the change would be redundant
-            if (oldLowWater == newLowWater && oldHighWater == newHighWater) {
-                return oldVal;
-            }
-            newVal = (long)newLowWater << CONN_LOW_BIT | (long)newHighWater << CONN_HIGH_BIT;
-        } while (! connectionStatusUpdater.compareAndSet(this, oldVal, newVal));
-        getIoThread().execute(new Runnable() {
-            @Override
-            public void run() {
-                if(openConnections >= getHighWater(connectionStatus)) {
-                    synchronized (QueuedNioTcpServer.this) {
-                        suspendedDueToWatermark = true;
-                        tcpServerConnectionLimitLog.logf(FQCN, Logger.Level.DEBUG, null, "Total open connections reach high water limit (%s) after updating water mark", getHighWater(connectionStatus));
-                    }
-                } else if(suspendedDueToWatermark && openConnections <= getLowWater(connectionStatus)) {
-                    suspendedDueToWatermark = false;
-                }
-            }
-        });
-        return oldVal;
-    }
-
-    private static int getHighWater(final long value) {
-        return (int) ((value & CONN_HIGH_MASK) >> CONN_HIGH_BIT);
-    }
-
-    private static int getLowWater(final long value) {
-        return (int) ((value & CONN_LOW_MASK) >> CONN_LOW_BIT);
-    }
-
-    public NioSocketStreamConnection accept() throws IOException {
-        final WorkerThread current = WorkerThread.getCurrent();
-        if (current == null) {
-            return null;
-        }
-        final BlockingQueue<SocketChannel> socketChannels = acceptQueues.get(current.getNumber());
-        final SocketChannel accepted;
-        boolean ok = false;
-        try {
-            accepted = socketChannels.poll();
-            if (accepted != null) try {
-                final SelectionKey selectionKey = current.registerChannel(accepted);
-                final NioSocketStreamConnection newConnection = new NioSocketStreamConnection(current, selectionKey, handle);
-                newConnection.setOption(Options.READ_TIMEOUT, Integer.valueOf(readTimeout));
-                newConnection.setOption(Options.WRITE_TIMEOUT, Integer.valueOf(writeTimeout));
-                ok = true;
-                return newConnection;
-            } finally {
-                if (! ok) {
-                    safeClose(accepted);
-                    handle.freeConnection();
-                }
-            }
-        } catch (IOException e) {
-            return null;
-        }
-        // by contract, only a resume will do
-        return null;
-    }
-
-    public String toString() {
-        return String.format("TCP server (NIO) <%s>", Integer.toHexString(hashCode()));
-    }
-
-    public ChannelListener<? super QueuedNioTcpServer> getAcceptListener() {
-        return acceptListener;
-    }
-
-    public void setAcceptListener(final ChannelListener<? super QueuedNioTcpServer> acceptListener) {
-        this.acceptListener = acceptListener;
-    }
-
-    public ChannelListener.Setter<QueuedNioTcpServer> getAcceptSetter() {
-        return new Setter<QueuedNioTcpServer>(this);
-    }
-
-    public boolean isOpen() {
-        return channel.isOpen();
-    }
-
-    public SocketAddress getLocalAddress() {
-        return socket.getLocalSocketAddress();
-    }
-
-    public <A extends SocketAddress> A getLocalAddress(final Class<A> type) {
-        final SocketAddress address = getLocalAddress();
-        return type.isInstance(address) ? type.cast(address) : null;
-    }
-
-    public void suspendAccepts() {
-        synchronized (this) {
-            handle.suspend(SelectionKey.OP_ACCEPT);
-            suspended = true;
-        }
-    }
-
-    public void resumeAccepts() {
-        synchronized (this) {
-            suspended = false;
-            handle.resume(SelectionKey.OP_ACCEPT);
-        }
-    }
-
-    public boolean isAcceptResumed() {
-        return !suspended;
-    }
-
-    public void wakeupAccepts() {
-        tcpServerLog.logf(FQCN, Logger.Level.TRACE, null, "Wake up accepts on %s", this);
-        resumeAccepts();
-        handle.wakeup(SelectionKey.OP_ACCEPT);
-    }
-
-    public void awaitAcceptable() throws IOException {
-        throw log.unsupported("awaitAcceptable");
-    }
-
-    public void awaitAcceptable(final long time, final TimeUnit timeUnit) throws IOException {
-        throw log.unsupported("awaitAcceptable");
-    }
-
-    @Deprecated
-    public XnioExecutor getAcceptThread() {
-        return getIoThread();
-    }
-
-    void handleReady() {
-        final SocketChannel accepted;
-        try {
-            accepted = channel.accept();
-            if(suspendedDueToWatermark) {
-                tcpServerConnectionLimitLog.logf(FQCN, Logger.Level.DEBUG, null, "Exceeding connection high water limit (%s). Closing this new accepting request %s", getHighWater(connectionStatus), accepted);
-                IoUtils.safeClose(accepted);
-                return;
-            }
-        } catch (ClosedChannelException e) {
-            tcpServerLog.logf(FQCN, Logger.Level.DEBUG, e, "ClosedChannelException occurred at accepting request on the server channel %s", channel);
-            return;
-        } catch (IOException e) {
-            tcpServerLog.logf(FQCN, Logger.Level.ERROR, e, "Exception accepting request, closing server channel %s", this);
-            IoUtils.safeClose(channel);
-            return;
-        }
-        try {
-            boolean ok = false;
-            if (accepted != null) try {
-                int hash = ThreadLocalRandom.current().nextInt();
-                accepted.configureBlocking(false);
-                final Socket socket = accepted.socket();
-                socket.setKeepAlive(keepAlive != 0);
-                socket.setOOBInline(oobInline != 0);
-                socket.setTcpNoDelay(tcpNoDelay != 0);
-                final int sendBuffer = this.sendBuffer;
-                if (sendBuffer > 0) socket.setSendBufferSize(sendBuffer);
-                final WorkerThread ioThread = worker.getIoThread(hash);
-                ok = true;
-                final int number = ioThread.getNumber();
-                final BlockingQueue<SocketChannel> queue = acceptQueues.get(number);
-                queue.add(accepted);
-                // todo: only execute if necessary
-                ioThread.execute(acceptTask);
-                openConnections++;
-                if(openConnections >= getHighWater(connectionStatus)) {
-                    synchronized (QueuedNioTcpServer.this) {
-                        suspendedDueToWatermark = true;
-                        if (limitwarn) {
-                            tcpServerConnectionLimitLog.logf(FQCN, Logger.Level.WARN, null, "Total open connections reach high water limit (%s) by this new accepting request %s", getHighWater(connectionStatus), accepted);
-                            limitwarn = false;
-                        } else {
-                            tcpServerConnectionLimitLog.logf(FQCN, Logger.Level.DEBUG, null, "Total open connections reach high water limit (%s) by this new accepting request %s", getHighWater(connectionStatus), accepted);
-                        }
-                    }
-                }
-            } finally {
-                if (! ok) safeClose(accepted);
-            }
-        } catch (IOException ignored) {
-        }
-    }
-
-    public void connectionClosed() {
-        thread.execute(connectionClosedTask);
-    }
-}


=====================================
nio-impl/src/main/java/org/xnio/nio/QueuedNioTcpServerHandle.java → nio-impl/src/test/java/org/xnio/nio/test/AbstractNioSslBufferExpansionTcpTest.java
=====================================
@@ -1,7 +1,7 @@
 /*
  * JBoss, Home of Professional Open Source
  *
- * Copyright 2015 Red Hat, Inc. and/or its affiliates.
+ * Copyright 2020 Red Hat, Inc. and/or its affiliates.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -15,46 +15,37 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package org.xnio.nio.test;
 
-package org.xnio.nio;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.Socket;
 
-import static org.xnio.IoUtils.safeClose;
-
-import java.nio.channels.SelectionKey;
+import org.xnio.ChannelListener;
+import org.xnio.XnioWorker;
+import org.xnio.channels.AcceptingChannel;
+import org.xnio.channels.ConnectedChannel;
+import org.xnio.channels.StreamSinkChannel;
+import org.xnio.channels.StreamSourceChannel;
 
 /**
- * @author <a href="mailto:david.lloyd at redhat.com">David M. Lloyd</a>
+ * Superclass for ssl tcp test cases to verify if
+ * {@link javax.net.ssl.SSLEngineResult.Status#BUFFER_OVERFLOW}
+ * is handled appropriately.
+ * 
+ * @author <a href="mailto:frainone at redhat.com">Flavia Rainone</a>
+ *
  */
-final class QueuedNioTcpServerHandle extends NioHandle implements ChannelClosed {
-
-    private final QueuedNioTcpServer server;
-
-    QueuedNioTcpServerHandle(final QueuedNioTcpServer server, final WorkerThread workerThread, final SelectionKey key, final int highWater, final int lowWater) {
-        super(workerThread, key);
-        this.server = server;
-    }
-
-    void handleReady(final int ops) {
-        server.handleReady();
-    }
-
-    void forceTermination() {
-        safeClose(server);
-    }
-
-    void terminated() {
-        server.invokeCloseHandler();
-    }
-
-    public void channelClosed() {
-        freeConnection();
-    }
-
-    void freeConnection() {
-       server.connectionClosed();
-    }
-
-    int getConnectionCount() {
-        return -1;
+public abstract class AbstractNioSslBufferExpansionTcpTest<T extends ConnectedChannel, R extends StreamSourceChannel, W extends StreamSinkChannel> extends AbstractNioSslTcpTest<T, R, W> {
+
+    @Override
+    protected AcceptingChannel<? extends T> startServer(XnioWorker worker, final ChannelListener<? super T> serverHandler) throws
+            IOException {
+        AcceptingChannel<? extends T> server = super.startServer(worker, serverHandler);
+        // server attack
+        Socket socket = new Socket("127.0.0.1", SERVER_PORT);
+        OutputStream outputStream = socket.getOutputStream();
+        outputStream.write(new byte[]{0x16, 0x3, 0x3, 0x71, 0x41}, 0, 5);
+        return server;
     }
 }


=====================================
nio-impl/src/test/java/org/xnio/nio/test/AbstractNioTcpTest.java
=====================================
@@ -26,6 +26,7 @@ import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.net.Inet4Address;
 import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
 import java.nio.ByteBuffer;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
@@ -88,13 +89,7 @@ public abstract class AbstractNioTcpTest<T extends ConnectedChannel, R extends S
             worker = xnio.createWorker(OptionMap.create(Options.WORKER_IO_THREADS, threads));
         }
         try {
-            final AcceptingChannel<? extends T> server = createServer(worker,
-                    new InetSocketAddress(Inet4Address.getByAddress(new byte[] { 127, 0, 0, 1 }), SERVER_PORT),
-                    ChannelListeners.<T>openListenerAdapter(new CatchingChannelListener<T>(
-                            serverHandler,
-                            problems
-                    )), serverOptionMap);
-            server.resumeAccepts();
+            final AcceptingChannel<? extends T> server = startServer(worker, serverHandler);
             try {
                 final IoFuture<? extends T> ioFuture = connect(worker, new InetSocketAddress(Inet4Address.getByAddress(new byte[] { 127, 0, 0, 1 }), SERVER_PORT), new CatchingChannelListener<T>(clientHandler, problems), null, clientOptionMap);
                 final T channel = ioFuture.get();
@@ -120,6 +115,18 @@ public abstract class AbstractNioTcpTest<T extends ConnectedChannel, R extends S
         }
     }
 
+    protected AcceptingChannel<? extends T> startServer(XnioWorker worker, final ChannelListener<? super T> serverHandler) throws
+            IOException {
+        final AcceptingChannel<? extends T> server = createServer(worker,
+                new InetSocketAddress(Inet4Address.getByAddress(new byte[] { 127, 0, 0, 1 }), SERVER_PORT),
+                ChannelListeners.<T>openListenerAdapter(new CatchingChannelListener<T>(
+                        serverHandler,
+                        problems
+                )), serverOptionMap);
+        server.resumeAccepts();
+        return server;
+    }
+
     /**
      * Set the number of threads that will be used by this test.
      * 


=====================================
nio-impl/src/test/java/org/xnio/nio/test/NioSslBufferExpansionTcpChannelTestCase.java
=====================================
@@ -0,0 +1,122 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2020 Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags.
+ *
+ * Licensed 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.xnio.nio.test;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.URL;
+
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.xnio.ChannelListener;
+import org.xnio.IoFuture;
+import org.xnio.OptionMap;
+import org.xnio.Xnio;
+import org.xnio.XnioWorker;
+import org.xnio.channels.AcceptingChannel;
+import org.xnio.channels.BoundChannel;
+import org.xnio.channels.ConnectedSslStreamChannel;
+import org.xnio.ssl.XnioSsl;
+
+/**
+ * Test for {@code XnioSsl} channels with a
+ * {@link javax.net.ssl.SSLEngineResult.Status#BUFFER_OVERFLOW} wrap result.
+ * 
+ * @author <a href="mailto:frainone at redhat.com">Flavia Rainone</a>
+ *
+ */
+public class NioSslBufferExpansionTcpChannelTestCase extends
+        AbstractNioSslBufferExpansionTcpTest<ConnectedSslStreamChannel, ConnectedSslStreamChannel, ConnectedSslStreamChannel> {
+
+    private XnioSsl xnioSsl;
+    private static final String KEY_STORE_PROPERTY = "javax.net.ssl.keyStore";
+    private static final String KEY_STORE_PASSWORD_PROPERTY = "javax.net.ssl.keyStorePassword";
+    private static final String TRUST_STORE_PROPERTY = "javax.net.ssl.trustStore";
+    private static final String TRUST_STORE_PASSWORD_PROPERTY = "javax.net.ssl.trustStorePassword";
+    private static final String DEFAULT_KEY_STORE = "keystore.jks";
+    private static final String DEFAULT_KEY_STORE_PASSWORD = "jboss-remoting-test";
+
+    @BeforeClass
+    public static void setKeyStoreAndTrustStore() {
+        final URL storePath = NioSslBufferExpansionTcpChannelTestCase.class.getClassLoader().getResource(DEFAULT_KEY_STORE);
+        if (System.getProperty(KEY_STORE_PROPERTY) == null) {
+            System.setProperty(KEY_STORE_PROPERTY, storePath.getFile());
+        }
+        if (System.getProperty(KEY_STORE_PASSWORD_PROPERTY) == null) {
+            System.setProperty(KEY_STORE_PASSWORD_PROPERTY, DEFAULT_KEY_STORE_PASSWORD);
+        }
+        if (System.getProperty(TRUST_STORE_PROPERTY) == null) {
+            System.setProperty(TRUST_STORE_PROPERTY, storePath.getFile());
+        }
+        if (System.getProperty(TRUST_STORE_PASSWORD_PROPERTY) == null) {
+            System.setProperty(TRUST_STORE_PASSWORD_PROPERTY, DEFAULT_KEY_STORE_PASSWORD);
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
+    protected AcceptingChannel<? extends ConnectedSslStreamChannel> createServer(XnioWorker worker, InetSocketAddress address,
+            ChannelListener<AcceptingChannel<ConnectedSslStreamChannel>> openListener, OptionMap optionMap) throws IOException {
+        return xnioSsl.createSslTcpServer(worker, address,  openListener,  optionMap);
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
+    protected IoFuture<? extends ConnectedSslStreamChannel> connect(XnioWorker worker, InetSocketAddress address,
+            ChannelListener<ConnectedSslStreamChannel> openListener, ChannelListener<? super BoundChannel> bindListener,
+            OptionMap optionMap) {
+        return xnioSsl.connectSsl(worker, address,  openListener, bindListener, optionMap);
+    }
+
+    @Override
+    protected void setReadListener(ConnectedSslStreamChannel channel, ChannelListener<ConnectedSslStreamChannel> readListener) {
+        channel.getReadSetter().set(readListener);
+    }
+
+    @Override
+    protected void setWriteListener(ConnectedSslStreamChannel channel, ChannelListener<ConnectedSslStreamChannel> writeListener) {
+        channel.getWriteSetter().set(writeListener);
+    }
+
+    @Override
+    protected void resumeReads(ConnectedSslStreamChannel channel) {
+        channel.resumeReads();
+    }
+
+    @Override
+    protected void resumeWrites(ConnectedSslStreamChannel channel) {
+        channel.resumeWrites();
+    }
+
+    @Override
+    protected void shutdownReads(ConnectedSslStreamChannel channel) throws IOException {
+        channel.shutdownReads();
+    }
+
+    @Override
+    protected void shutdownWrites(ConnectedSslStreamChannel channel) throws IOException {
+        channel.shutdownWrites();
+    }
+
+    @Override
+    protected void doConnectionTest(final Runnable body, final ChannelListener<? super ConnectedSslStreamChannel> clientHandler, final ChannelListener<? super ConnectedSslStreamChannel> serverHandler) throws Exception {
+        xnioSsl = Xnio.getInstance("nio", NioSslBufferExpansionTcpChannelTestCase.class.getClassLoader()).getSslProvider(OptionMap.EMPTY);
+        super.doConnectionTest(body,  clientHandler, serverHandler);
+    }
+}


=====================================
nio-impl/src/test/java/org/xnio/nio/test/NioSslBufferExpansionTcpConnectionTestCase.java
=====================================
@@ -0,0 +1,119 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2020 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.xnio.nio.test;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.URL;
+
+import org.junit.BeforeClass;
+import org.xnio.ChannelListener;
+import org.xnio.IoFuture;
+import org.xnio.OptionMap;
+import org.xnio.Xnio;
+import org.xnio.XnioWorker;
+import org.xnio.channels.AcceptingChannel;
+import org.xnio.channels.BoundChannel;
+import org.xnio.conduits.ConduitStreamSinkChannel;
+import org.xnio.conduits.ConduitStreamSourceChannel;
+import org.xnio.ssl.SslConnection;
+import org.xnio.ssl.XnioSsl;
+
+/**
+ * Test for {@code XnioSsl} connections with a
+ * {@link javax.net.ssl.SSLEngineResult.Status#BUFFER_OVERFLOW} wrap result.
+ * 
+ * @author <a href="mailto:frainone at redhat.com">Flavia Rainone</a>
+ *
+ */
+public class NioSslBufferExpansionTcpConnectionTestCase extends
+        AbstractNioSslBufferExpansionTcpTest<SslConnection, ConduitStreamSourceChannel, ConduitStreamSinkChannel> {
+    private XnioSsl xnioSsl;
+    private static final String KEY_STORE_PROPERTY = "javax.net.ssl.keyStore";
+    private static final String KEY_STORE_PASSWORD_PROPERTY = "javax.net.ssl.keyStorePassword";
+    private static final String TRUST_STORE_PROPERTY = "javax.net.ssl.trustStore";
+    private static final String TRUST_STORE_PASSWORD_PROPERTY = "javax.net.ssl.trustStorePassword";
+    private static final String DEFAULT_KEY_STORE = "keystore.jks";
+    private static final String DEFAULT_KEY_STORE_PASSWORD = "jboss-remoting-test";
+
+    @BeforeClass
+    public static void setKeyStoreAndTrustStore() {
+        final URL storePath = NioSslTcpChannelTestCase.class.getClassLoader().getResource(DEFAULT_KEY_STORE);
+        if (System.getProperty(KEY_STORE_PROPERTY) == null) {
+            System.setProperty(KEY_STORE_PROPERTY, storePath.getFile());
+        }
+        if (System.getProperty(KEY_STORE_PASSWORD_PROPERTY) == null) {
+            System.setProperty(KEY_STORE_PASSWORD_PROPERTY, DEFAULT_KEY_STORE_PASSWORD);
+        }
+        if (System.getProperty(TRUST_STORE_PROPERTY) == null) {
+            System.setProperty(TRUST_STORE_PROPERTY, storePath.getFile());
+        }
+        if (System.getProperty(TRUST_STORE_PASSWORD_PROPERTY) == null) {
+            System.setProperty(TRUST_STORE_PASSWORD_PROPERTY, DEFAULT_KEY_STORE_PASSWORD);
+        }
+    }
+
+    @Override
+    protected AcceptingChannel<? extends SslConnection> createServer(XnioWorker worker, InetSocketAddress address,
+            ChannelListener<AcceptingChannel<SslConnection>> openListener, OptionMap optionMap) throws IOException {
+        return xnioSsl.createSslConnectionServer(worker, address,  openListener,  optionMap);
+    }
+
+    @Override
+    protected IoFuture<? extends SslConnection> connect(XnioWorker worker, InetSocketAddress address,
+            ChannelListener<SslConnection> openListener, ChannelListener<? super BoundChannel> bindListener,
+            OptionMap optionMap) {
+        return xnioSsl.openSslConnection(worker, address,  openListener, bindListener, optionMap);
+    }
+
+    @Override
+    protected void setReadListener(SslConnection connection, ChannelListener<ConduitStreamSourceChannel> readListener) {
+        connection.getSourceChannel().setReadListener(readListener);
+    }
+
+    @Override
+    protected void setWriteListener(SslConnection connection, ChannelListener<ConduitStreamSinkChannel> writeListener) {
+        connection.getSinkChannel().setWriteListener(writeListener);
+    }
+
+    @Override
+    protected void resumeReads(SslConnection connection) {
+        connection.getSourceChannel().resumeReads();
+    }
+
+    @Override
+    protected void resumeWrites(SslConnection connection) {
+        connection.getSinkChannel().resumeWrites();
+    }
+
+    @Override
+    protected void shutdownReads(SslConnection connection) throws IOException {
+        connection.getSourceChannel().shutdownReads();
+    }
+
+    @Override
+    protected void shutdownWrites(SslConnection connection) throws IOException {
+        connection.getSinkChannel().shutdownWrites();
+    }
+
+    @Override
+    protected void doConnectionTest(final Runnable body, final ChannelListener<? super SslConnection> clientHandler, final ChannelListener<? super SslConnection> serverHandler) throws Exception {
+        xnioSsl = Xnio.getInstance("nio", NioSslTcpChannelTestCase.class.getClassLoader()).getSslProvider(OptionMap.EMPTY);
+        super.doConnectionTest(body,  clientHandler, serverHandler);
+    }
+}


=====================================
nio-impl/src/test/java/org/xnio/nio/test/NioSslRandomlyTimedBufferExpansionTcpChannelTestCase.java
=====================================
@@ -0,0 +1,141 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2020 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.xnio.nio.test;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.URL;
+
+import org.junit.BeforeClass;
+import org.xnio.ChannelListener;
+import org.xnio.IoFuture;
+import org.xnio.OptionMap;
+import org.xnio.Xnio;
+import org.xnio.XnioWorker;
+import org.xnio.channels.AcceptingChannel;
+import org.xnio.channels.BoundChannel;
+import org.xnio.channels.ConnectedSslStreamChannel;
+import org.xnio.ssl.XnioSsl;
+
+/**
+ * Test for {@code XnioSsl} channel with a
+ * {@link javax.net.ssl.SSLEngineResult.Status#BUFFER_OVERFLOW} result
+ * presented at a random time.
+ * 
+ * @author <a href="mailto:frainone at redhat.com">Flavia Rainone</a>
+ *
+ */
+public class NioSslRandomlyTimedBufferExpansionTcpChannelTestCase extends AbstractNioSslTcpTest<ConnectedSslStreamChannel, ConnectedSslStreamChannel, ConnectedSslStreamChannel>{
+    private XnioSsl xnioSsl;
+    private static final String KEY_STORE_PROPERTY = "javax.net.ssl.keyStore";
+    private static final String KEY_STORE_PASSWORD_PROPERTY = "javax.net.ssl.keyStorePassword";
+    private static final String TRUST_STORE_PROPERTY = "javax.net.ssl.trustStore";
+    private static final String TRUST_STORE_PASSWORD_PROPERTY = "javax.net.ssl.trustStorePassword";
+    private static final String DEFAULT_KEY_STORE = "keystore.jks";
+    private static final String DEFAULT_KEY_STORE_PASSWORD = "jboss-remoting-test";
+
+    @Override
+    protected AcceptingChannel<? extends ConnectedSslStreamChannel> startServer(XnioWorker worker, final ChannelListener<? super ConnectedSslStreamChannel> serverHandler) throws
+            IOException {
+        AcceptingChannel<? extends ConnectedSslStreamChannel> server = super.startServer(worker, serverHandler);
+        // server atack
+        Thread thread = new Thread(() -> {
+            final Socket socket;
+            try {
+                Thread.sleep((long) (Math.random()*300));
+                socket = new Socket("127.0.0.1", SERVER_PORT);
+                OutputStream outputStream = socket.getOutputStream();
+                outputStream.write(new byte[]{0x16, 0x3, 0x3, 0x71, 0x41}, 0, 5);
+            } catch (IOException | InterruptedException e) {
+                e.printStackTrace();
+            }});
+        thread.start();
+
+        return server;
+    }
+
+    @BeforeClass
+    public static void setKeyStoreAndTrustStore() {
+        final URL storePath = NioSslTcpChannelTestCase.class.getClassLoader().getResource(DEFAULT_KEY_STORE);
+        if (System.getProperty(KEY_STORE_PROPERTY) == null) {
+            System.setProperty(KEY_STORE_PROPERTY, storePath.getFile());
+        }
+        if (System.getProperty(KEY_STORE_PASSWORD_PROPERTY) == null) {
+            System.setProperty(KEY_STORE_PASSWORD_PROPERTY, DEFAULT_KEY_STORE_PASSWORD);
+        }
+        if (System.getProperty(TRUST_STORE_PROPERTY) == null) {
+            System.setProperty(TRUST_STORE_PROPERTY, storePath.getFile());
+        }
+        if (System.getProperty(TRUST_STORE_PASSWORD_PROPERTY) == null) {
+            System.setProperty(TRUST_STORE_PASSWORD_PROPERTY, DEFAULT_KEY_STORE_PASSWORD);
+        }
+    }
+
+    @Override
+    @SuppressWarnings("deprecation")
+    protected AcceptingChannel<? extends ConnectedSslStreamChannel> createServer(XnioWorker worker, InetSocketAddress address,
+            ChannelListener<AcceptingChannel<ConnectedSslStreamChannel>> openListener, OptionMap optionMap) throws IOException {
+        return xnioSsl.createSslTcpServer(worker, address,  openListener,  optionMap);
+    }
+
+    @Override
+    @SuppressWarnings("deprecation")
+    protected IoFuture<? extends ConnectedSslStreamChannel> connect(XnioWorker worker, InetSocketAddress address,
+            ChannelListener<ConnectedSslStreamChannel> openListener, ChannelListener<? super BoundChannel> bindListener,
+            OptionMap optionMap) {
+        return xnioSsl.connectSsl(worker, address,  openListener, bindListener, optionMap);
+    }
+
+    @Override
+    protected void setReadListener(ConnectedSslStreamChannel channel, ChannelListener<ConnectedSslStreamChannel> readListener) {
+        channel.getReadSetter().set(readListener);
+    }
+
+    @Override
+    protected void setWriteListener(ConnectedSslStreamChannel channel, ChannelListener<ConnectedSslStreamChannel> writeListener) {
+        channel.getWriteSetter().set(writeListener);
+    }
+
+    @Override
+    protected void resumeReads(ConnectedSslStreamChannel channel) {
+        channel.resumeReads();
+    }
+
+    @Override
+    protected void resumeWrites(ConnectedSslStreamChannel channel) {
+        channel.resumeWrites();
+    }
+
+    @Override
+    protected void shutdownReads(ConnectedSslStreamChannel channel) throws IOException {
+        channel.shutdownReads();
+    }
+
+    @Override
+    protected void shutdownWrites(ConnectedSslStreamChannel channel) throws IOException {
+        channel.shutdownWrites();
+    }
+
+    @Override
+    protected void doConnectionTest(final Runnable body, final ChannelListener<? super ConnectedSslStreamChannel> clientHandler, final ChannelListener<? super ConnectedSslStreamChannel> serverHandler) throws Exception {
+        xnioSsl = Xnio.getInstance("nio", NioSslTcpChannelTestCase.class.getClassLoader()).getSslProvider(OptionMap.EMPTY);
+        super.doConnectionTest(body,  clientHandler, serverHandler);
+    }
+}


=====================================
nio-impl/src/test/java/org/xnio/nio/test/NioSslRandomlyTimedBufferExpansionTcpConnectionTestCase.java
=====================================
@@ -0,0 +1,141 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2020 Red Hat, Inc. and/or its affiliates.
+ *
+ * Licensed 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.xnio.nio.test;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.URL;
+
+import org.junit.BeforeClass;
+import org.xnio.ChannelListener;
+import org.xnio.IoFuture;
+import org.xnio.OptionMap;
+import org.xnio.Xnio;
+import org.xnio.XnioWorker;
+import org.xnio.channels.AcceptingChannel;
+import org.xnio.channels.BoundChannel;
+import org.xnio.conduits.ConduitStreamSinkChannel;
+import org.xnio.conduits.ConduitStreamSourceChannel;
+import org.xnio.ssl.SslConnection;
+import org.xnio.ssl.XnioSsl;
+
+/**
+ * Test for {@code XnioSsl} connection with a
+ * {@link javax.net.ssl.SSLEngineResult.Status#BUFFER_OVERFLOW} result
+ * presented at a random time.
+ * 
+ * @author <a href="mailto:frainone at redhat.com">Flavia Rainone</a>
+ *
+ */
+public class NioSslRandomlyTimedBufferExpansionTcpConnectionTestCase extends AbstractNioSslTcpTest<SslConnection, ConduitStreamSourceChannel, ConduitStreamSinkChannel>{
+    private XnioSsl xnioSsl;
+    private static final String KEY_STORE_PROPERTY = "javax.net.ssl.keyStore";
+    private static final String KEY_STORE_PASSWORD_PROPERTY = "javax.net.ssl.keyStorePassword";
+    private static final String TRUST_STORE_PROPERTY = "javax.net.ssl.trustStore";
+    private static final String TRUST_STORE_PASSWORD_PROPERTY = "javax.net.ssl.trustStorePassword";
+    private static final String DEFAULT_KEY_STORE = "keystore.jks";
+    private static final String DEFAULT_KEY_STORE_PASSWORD = "jboss-remoting-test";
+
+    @Override
+    protected AcceptingChannel<? extends SslConnection> startServer(XnioWorker worker, final ChannelListener<? super SslConnection> serverHandler) throws
+            IOException {
+        AcceptingChannel<? extends SslConnection> server = super.startServer(worker, serverHandler);
+        // server atack
+        Thread thread = new Thread(() -> {
+            final Socket socket;
+            try {
+                Thread.sleep((long) (Math.random()*300));
+                socket = new Socket("127.0.0.1", SERVER_PORT);
+                OutputStream outputStream = socket.getOutputStream();
+                outputStream.write(new byte[]{0x16, 0x3, 0x3, 0x71, 0x41}, 0, 5);
+            } catch (IOException | InterruptedException e) {
+                e.printStackTrace();
+            }});
+        thread.start();
+
+        return server;
+    }
+
+    @BeforeClass
+    public static void setKeyStoreAndTrustStore() {
+        final URL storePath = NioSslTcpChannelTestCase.class.getClassLoader().getResource(DEFAULT_KEY_STORE);
+        if (System.getProperty(KEY_STORE_PROPERTY) == null) {
+            System.setProperty(KEY_STORE_PROPERTY, storePath.getFile());
+        }
+        if (System.getProperty(KEY_STORE_PASSWORD_PROPERTY) == null) {
+            System.setProperty(KEY_STORE_PASSWORD_PROPERTY, DEFAULT_KEY_STORE_PASSWORD);
+        }
+        if (System.getProperty(TRUST_STORE_PROPERTY) == null) {
+            System.setProperty(TRUST_STORE_PROPERTY, storePath.getFile());
+        }
+        if (System.getProperty(TRUST_STORE_PASSWORD_PROPERTY) == null) {
+            System.setProperty(TRUST_STORE_PASSWORD_PROPERTY, DEFAULT_KEY_STORE_PASSWORD);
+        }
+    }
+
+    @Override
+    protected AcceptingChannel<? extends SslConnection> createServer(XnioWorker worker, InetSocketAddress address,
+            ChannelListener<AcceptingChannel<SslConnection>> openListener, OptionMap optionMap) throws IOException {
+        return xnioSsl.createSslConnectionServer(worker, address,  openListener,  optionMap);
+    }
+
+    @Override
+    protected IoFuture<? extends SslConnection> connect(XnioWorker worker, InetSocketAddress address,
+            ChannelListener<SslConnection> openListener, ChannelListener<? super BoundChannel> bindListener,
+            OptionMap optionMap) {
+        return xnioSsl.openSslConnection(worker, address,  openListener, bindListener, optionMap);
+    }
+
+    @Override
+    protected void setReadListener(SslConnection connection, ChannelListener<ConduitStreamSourceChannel> readListener) {
+        connection.getSourceChannel().setReadListener(readListener);
+    }
+
+    @Override
+    protected void setWriteListener(SslConnection connection, ChannelListener<ConduitStreamSinkChannel> writeListener) {
+        connection.getSinkChannel().setWriteListener(writeListener);
+    }
+
+    @Override
+    protected void resumeReads(SslConnection connection) {
+        connection.getSourceChannel().resumeReads();
+    }
+
+    @Override
+    protected void resumeWrites(SslConnection connection) {
+        connection.getSinkChannel().resumeWrites();
+    }
+
+    @Override
+    protected void shutdownReads(SslConnection connection) throws IOException {
+        connection.getSourceChannel().shutdownReads();
+    }
+
+    @Override
+    protected void shutdownWrites(SslConnection connection) throws IOException {
+        connection.getSinkChannel().shutdownWrites();
+    }
+
+    @Override
+    protected void doConnectionTest(final Runnable body, final ChannelListener<? super SslConnection> clientHandler, final ChannelListener<? super SslConnection> serverHandler) throws Exception {
+        xnioSsl = Xnio.getInstance("nio", NioSslTcpChannelTestCase.class.getClassLoader()).getSslProvider(OptionMap.EMPTY);
+        super.doConnectionTest(body,  clientHandler, serverHandler);
+    }
+}


=====================================
pom.xml
=====================================
@@ -32,7 +32,7 @@
     <artifactId>xnio-all</artifactId>
     <packaging>pom</packaging>
     <name>XNIO Parent POM</name>
-    <version>3.7.7.Final</version>
+    <version>3.8.0.Final</version>
     <description>The aggregator POM of the XNIO project</description>
 
     <licenses>
@@ -49,16 +49,16 @@
     </modules>
 
     <properties>
-        <byteman-version>4.0.6</byteman-version>
-        <version.org.jboss.logging.jboss-logging>3.4.0.Final</version.org.jboss.logging.jboss-logging>
-        <version.org.jboss.logging.jboss-logging-tools>2.2.0.Final</version.org.jboss.logging.jboss-logging-tools>
-        <version.org.jboss.logmanager.jboss-logmanager>2.1.10.Final</version.org.jboss.logmanager.jboss-logmanager>
-        <version.org.jboss.threads>2.3.0.Beta2</version.org.jboss.threads>
+        <byteman-version>4.0.8</byteman-version>
+        <version.org.jboss.logging.jboss-logging>3.4.1.Final</version.org.jboss.logging.jboss-logging>
+        <version.org.jboss.logging.jboss-logging-tools>2.2.1.Final</version.org.jboss.logging.jboss-logging-tools>
+        <version.org.jboss.logmanager.jboss-logmanager>2.1.14.Final</version.org.jboss.logmanager.jboss-logmanager>
+        <version.org.jboss.threads>2.3.3.Final</version.org.jboss.threads>
         <version.org.wildfly.common>1.5.2.Final</version.org.wildfly.common>
-        <version.org.wildfly.client-config>1.0.0.Final</version.org.wildfly.client-config>
-        <version.bridger.plugin>1.1.Final</version.bridger.plugin>
-        <version.junit>4.11</version.junit>
-        <version.jmock>2.6.0</version.jmock>
+        <version.org.wildfly.client-config>1.0.1.Final</version.org.wildfly.client-config>
+        <version.bridger.plugin>1.5.Final</version.bridger.plugin>
+        <version.junit>4.12</version.junit>
+        <version.jmock>2.12.0</version.jmock>
         <org.xnio.ssl.new>false</org.xnio.ssl.new>
 
         <version.org.osgi.core>6.0.0</version.org.osgi.core>



View it on GitLab: https://salsa.debian.org/java-team/jboss-xnio/-/compare/b99bec6524f8564641c46a43a3fedac5ce2a24fe...afe9826d3e3997a7f4e9b2032a9a2e262be1884b

-- 
View it on GitLab: https://salsa.debian.org/java-team/jboss-xnio/-/compare/b99bec6524f8564641c46a43a3fedac5ce2a24fe...afe9826d3e3997a7f4e9b2032a9a2e262be1884b
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-java-commits/attachments/20200327/adb1bbee/attachment.html>


More information about the pkg-java-commits mailing list