This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch mina-2.2.X-DIRMINA-1199
in repository https://gitbox.apache.org/repos/asf/mina.git

commit a402bb71196e51936a9df12c6c663956f5f63c53
Author: emmanuel lecharny <[email protected]>
AuthorDate: Sun Oct 11 22:58:42 2026 +0200

    Fixed the mPendingError handling, moving around some methods to the paren't 
class
---
 .../org/apache/mina/filter/ssl/SSLHandlerG0.java   |  61 +++--
 .../org/apache/mina/filter/ssl/SSLHandlerG1.java   |  72 +++---
 .../org/apache/mina/filter/ssl/SslHandler.java     |  66 ++++++
 .../org/apache/mina/filter/ssl/SslFilterTest.java  | 263 +++++++++++++++++++++
 4 files changed, 409 insertions(+), 53 deletions(-)

diff --git 
a/mina-core/src/main/java/org/apache/mina/filter/ssl/SSLHandlerG0.java 
b/mina-core/src/main/java/org/apache/mina/filter/ssl/SSLHandlerG0.java
index ec5653245..3fee601df 100644
--- a/mina-core/src/main/java/org/apache/mina/filter/ssl/SSLHandlerG0.java
+++ b/mina-core/src/main/java/org/apache/mina/filter/ssl/SSLHandlerG0.java
@@ -97,7 +97,7 @@ import org.apache.mina.core.write.WriteRequest;
     /**
      * Captured error state
      */
-    protected SSLException mPendingError = null;
+    //protected SSLException mPendingError = null;
 
     /**
      * Instantiates a new handler
@@ -207,6 +207,15 @@ import org.apache.mina.core.write.WriteRequest;
             }
         }
 
+        // The message is null when we have had an exception
+        if (message==null) {
+            if ( mPendingError != null ) {
+                throw mPendingError;
+            } else {
+                throw new IllegalStateException("closed");
+            }
+        }
+
         IoBuffer source = message;
         
         // No need to fo for another loop if the message is empty
@@ -459,16 +468,16 @@ import org.apache.mina.core.write.WriteRequest;
      * 
      * @throws SSLException
      */
-    synchronized protected boolean write_handshake(NextFilter next) throws 
SSLException {
-        if (LOGGER.isDebugEnabled()) {
-            LOGGER.debug("{} write_handshake() - internal", toString());
-        }
-
-        IoBuffer source = ZERO;
-        IoBuffer dest = allocate_encode_buffer(source.remaining());
-        
-        return write_handshake_loop(next, source, dest);
-    }
+//    synchronized protected boolean write_handshake(NextFilter next) throws 
SSLException {
+//        if (LOGGER.isDebugEnabled()) {
+//            LOGGER.debug("{} write_handshake() - internal", toString());
+//        }
+//
+//        IoBuffer source = ZERO;
+//        IoBuffer dest = allocate_encode_buffer(source.remaining());
+//        
+//        return write_handshake_loop(next, source, dest);
+//    }
 
     /**
      * Attempts to generate a handshake message and write the data to the 
IoSession.
@@ -484,6 +493,7 @@ import org.apache.mina.core.write.WriteRequest;
      * 
      * @throws SSLException
      */
+    @Override
     @SuppressWarnings("incomplete-switch")
     protected boolean write_handshake_loop(NextFilter next, IoBuffer source, 
IoBuffer dest) throws SSLException {
         if (mOutboundClosing && mEngine.isOutboundDone()) {
@@ -681,19 +691,22 @@ import org.apache.mina.core.write.WriteRequest;
      * @param next The next filter in the chain
      * @throws SSLException The rethrown pending error
      */
-    synchronized protected void throw_pending_error(NextFilter next) throws 
SSLException {
-        SSLException sslException = mPendingError;
-        
-        if (sslException != null) {
-            // Loop to send back the alert messages
-            receive_loop(next, null);
-            
-            mPendingError = null;
-            
-            // And finally rethrow the exception 
-            throw sslException;
-        }
-    }
+//    synchronized protected void throw_pending_error(NextFilter next) throws 
SSLException {
+//        SSLException sslException = mPendingError;
+//        
+//        if (sslException != null) {
+//            // Errors are fatal: the pending error stays set and is rethrown 
on every subsequent call.
+//            if (mEngine.isInboundDone() && mEngine.getHandshakeStatus() == 
HandshakeStatus.NEED_WRAP) {
+//                if (LOGGER.isDebugEnabled()) {
+//                    LOGGER.debug("{} throw_pending_error() - handshake needs 
wrap, invoking write", toString());
+//                }
+//                
+//                write_handshake(next);
+//            }
+//
+//            throw sslException;
+//        }
+//    }
 
     /**
      * Store any error we've got during the handshake or message handling
diff --git 
a/mina-core/src/main/java/org/apache/mina/filter/ssl/SSLHandlerG1.java 
b/mina-core/src/main/java/org/apache/mina/filter/ssl/SSLHandlerG1.java
index b2eae3fc2..ae518bf17 100644
--- a/mina-core/src/main/java/org/apache/mina/filter/ssl/SSLHandlerG1.java
+++ b/mina-core/src/main/java/org/apache/mina/filter/ssl/SSLHandlerG1.java
@@ -29,6 +29,7 @@ import org.apache.mina.filter.FilterEvent;
 import javax.net.ssl.SSLEngine;
 import javax.net.ssl.SSLEngineResult;
 import javax.net.ssl.SSLException;
+
 import java.nio.BufferOverflowException;
 import java.util.ArrayList;
 import java.util.Deque;
@@ -110,11 +111,11 @@ import java.util.concurrent.Executor;
      * Pending filter events for dispatching
      */
     protected final Deque<FilterEvent> mEventQueue = new 
ConcurrentLinkedDeque<>();
-
-    /**
-     * Captured error state
-     */
-    protected SSLException mPendingError = null;
+//
+//    /**
+//     * Captured error state
+//     */
+//    protected SSLException mPendingError = null;
 
     /**
      * Instantiates a new handler
@@ -237,10 +238,19 @@ import java.util.concurrent.Executor;
                 throw new IllegalStateException("closed");
             }
         }
+        
+        // The message is null when we have had an exception
+        if (message==null) {
+            if ( mPendingError != null ) {
+                throw mPendingError;
+            } else {
+                throw new IllegalStateException("closed");
+            }
+        }
 
         IoBuffer source = message;
         
-        // No need to fo for another loop if the message is empty
+        // No need to go for another loop if the message is empty
         if (source.remaining() == 0) {
             return;
         }
@@ -499,16 +509,16 @@ import java.util.concurrent.Executor;
      * 
      * @throws SSLException
      */
-    synchronized protected boolean write_handshake(NextFilter next) throws 
SSLException {
-        if (LOGGER.isDebugEnabled()) {
-            LOGGER.debug("{} write_handshake() - internal", toString());
-        }
-
-        IoBuffer source = ZERO;
-        IoBuffer dest = allocate_encode_buffer(source.remaining());
-        
-        return write_handshake_loop(next, source, dest);
-    }
+//    synchronized protected boolean write_handshake(NextFilter next) throws 
SSLException {
+//        if (LOGGER.isDebugEnabled()) {
+//            LOGGER.debug("{} write_handshake() - internal", toString());
+//        }
+//
+//        IoBuffer source = ZERO;
+//        IoBuffer dest = allocate_encode_buffer(source.remaining());
+//        
+//        return write_handshake_loop(next, source, dest);
+//    }
 
     /**
      * Attempts to generate a handshake message and write the data to the 
IoSession.
@@ -524,6 +534,7 @@ import java.util.concurrent.Executor;
      * 
      * @throws SSLException
      */
+    @Override
     @SuppressWarnings("incomplete-switch")
     protected boolean write_handshake_loop(NextFilter next, IoBuffer source, 
IoBuffer dest) throws SSLException {
         if (mOutboundClosing && mEngine.isOutboundDone()) {
@@ -734,19 +745,22 @@ import java.util.concurrent.Executor;
      * @param next The next filter in the chain
      * @throws SSLException The rethrown pending error
      */
-    synchronized protected void throw_pending_error(NextFilter next) throws 
SSLException {
-        SSLException sslException = mPendingError;
-        if (sslException != null) {
-            if (LOGGER.isDebugEnabled()) {
-                LOGGER.debug("{} throw_pending_error() - throwing pending 
error");
-            }
-            // Loop to send back the alert messages
-            receive_loop(next, null);
-            mPendingError = null;
-            // And finally rethrow the exception 
-            throw sslException;
-        }
-    }
+//    synchronized protected void throw_pending_error(NextFilter next) throws 
SSLException {
+//        SSLException sslException = mPendingError;
+//        
+//        if (sslException != null) {
+//            // Errors are fatal: the pending error stays set and is rethrown 
on every subsequent call.
+//            if (mEngine.isInboundDone() && mEngine.getHandshakeStatus() == 
HandshakeStatus.NEED_WRAP) {
+//                if (LOGGER.isDebugEnabled()) {
+//                    LOGGER.debug("{} throw_pending_error() - handshake needs 
wrap, invoking write", toString());
+//                }
+//                
+//                write_handshake(next);
+//            }
+//
+//            throw sslException;
+//        }
+//    }
 
     /**
      * Store any error we've got during the handshake or message handling
diff --git a/mina-core/src/main/java/org/apache/mina/filter/ssl/SslHandler.java 
b/mina-core/src/main/java/org/apache/mina/filter/ssl/SslHandler.java
index 1e6d13735..21426cc76 100644
--- a/mina-core/src/main/java/org/apache/mina/filter/ssl/SslHandler.java
+++ b/mina-core/src/main/java/org/apache/mina/filter/ssl/SslHandler.java
@@ -26,6 +26,7 @@ import java.util.concurrent.Executor;
 import javax.net.ssl.SSLEngine;
 import javax.net.ssl.SSLException;
 import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
 
 import org.apache.mina.core.buffer.IoBuffer;
 import org.apache.mina.core.filterchain.IoFilter.NextFilter;
@@ -93,6 +94,11 @@ public abstract class SslHandler {
      */
     protected IoBuffer mDecodeBuffer;
 
+    /**
+     * Captured error state
+     */
+    protected SSLException mPendingError = null;
+
     /**
      * Instantiates a new handler
      * 
@@ -176,6 +182,22 @@ public abstract class SslHandler {
      */
     abstract public void close(NextFilter next, final boolean linger) throws 
SSLException;
 
+    /**
+     * Attempts to generate a handshake message and write the data to the 
IoSession.
+     * <p>
+     * If FAST_HANDSHAKE is enabled, this method will recursively loop in 
order to
+     * combine multiple messages into one buffer.
+     * 
+     * @param next
+     * @param source
+     * @param dest
+     * 
+     * @return {@code true} if a message was generated and written
+     * 
+     * @throws SSLException
+     */
+    protected abstract boolean write_handshake_loop(NextFilter next, IoBuffer 
source, IoBuffer dest) throws SSLException;
+
     /**
      * {@inheritDoc}
      */
@@ -281,4 +303,48 @@ public abstract class SslHandler {
         int packets = 1 + (estimate / session.getPacketBufferSize());
         return IoBuffer.allocate(packets * session.getApplicationBufferSize());
     }
+    
+
+    /**
+     * Process the pending error and loop to send the associated alert if we 
have some.
+     * 
+     * @param next The next filter in the chain
+     * @throws SSLException The rethrown pending error
+     */
+    synchronized protected void throw_pending_error(NextFilter next) throws 
SSLException {
+        SSLException sslException = mPendingError;
+        
+        if (sslException != null) {
+            // Errors are fatal: the pending error stays set and is rethrown 
on every subsequent call.
+            if (mEngine.isInboundDone() && mEngine.getHandshakeStatus() == 
HandshakeStatus.NEED_WRAP) {
+                if (LOGGER.isDebugEnabled()) {
+                    LOGGER.debug("{} throw_pending_error() - handshake needs 
wrap, invoking write", toString());
+                }
+                
+                write_handshake(next);
+            }
+
+            throw sslException;
+        }
+    }
+
+    /**
+     * Attempts to generate a handshake message and write the data to the 
IoSession
+     * 
+     * @param next
+     * 
+     * @return {@code true} if a message was generated and written
+     * 
+     * @throws SSLException
+     */
+    synchronized protected boolean write_handshake(NextFilter next) throws 
SSLException {
+        if (LOGGER.isDebugEnabled()) {
+            LOGGER.debug("{} write_handshake() - internal", toString());
+        }
+
+        IoBuffer source = ZERO;
+        IoBuffer dest = allocate_encode_buffer(source.remaining());
+        
+        return write_handshake_loop(next, source, dest);
+    }
 }
diff --git 
a/mina-core/src/test/java/org/apache/mina/filter/ssl/SslFilterTest.java 
b/mina-core/src/test/java/org/apache/mina/filter/ssl/SslFilterTest.java
new file mode 100644
index 000000000..1d3d4dcf8
--- /dev/null
+++ b/mina-core/src/test/java/org/apache/mina/filter/ssl/SslFilterTest.java
@@ -0,0 +1,263 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.mina.filter.ssl;
+
+import java.net.InetSocketAddress;
+import java.security.KeyStore;
+import java.security.SecureRandom;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.TrustManagerFactory;
+
+import org.apache.mina.core.buffer.IoBuffer;
+import org.apache.mina.core.future.IoFuture;
+import org.apache.mina.core.service.IoAcceptor;
+import org.apache.mina.core.service.IoConnector;
+import org.apache.mina.core.service.IoHandlerAdapter;
+import org.apache.mina.core.session.IoSession;
+import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
+import org.apache.mina.transport.socket.nio.NioSocketConnector;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.junit.Assert.assertTrue;
+
+public class SslFilterTest {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(SslFilterTest.class);
+
+    @Test
+    public void transferLargeData() throws Exception {
+        KeyManagerFactory kmf = 
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+        TrustManagerFactory tmf = 
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+
+        KeyStore ks = KeyStore.getInstance("JKS");
+        KeyStore ts = KeyStore.getInstance("JKS");
+
+        final char[] password = "password".toCharArray();
+
+        ks.load(this.getClass().getResourceAsStream("keystore.jks"), password);
+        ts.load(this.getClass().getResourceAsStream("truststore.jks"), 
password);
+
+        kmf.init(ks, password);
+        tmf.init(ts);
+
+        final SSLContext contextClient = SSLContext.getInstance("TLSv1.3");
+        contextClient.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new 
SecureRandom());
+
+        final MySslFilter filterClient = new MySslFilter(contextClient);
+        filterClient.setEnabledCipherSuites(new String[] { 
"TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384" });
+        filterClient.setEnabledProtocols(new String[] { "TLSv1.3" });
+        filterClient.setUseNonBlockingPipeline( true );
+
+
+        final IoConnector socketConnector = new NioSocketConnector();
+
+        socketConnector.getFilterChain().addFirst("ssl", filterClient);
+        socketConnector.setHandler(new DebugFilter());
+
+        final SSLContext contextServer = SSLContext.getInstance("TLSv1.3");
+        contextServer.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new 
SecureRandom());
+
+        final MySslFilter filterServer = new MySslFilter(contextServer);
+        filterServer.setEnabledCipherSuites(new String[] { 
"TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384" });
+        filterServer.setEnabledProtocols(new String[] { "TLSv1.3" });
+        filterServer.setUseNonBlockingPipeline( false );
+
+        final IoAcceptor socketAcceptor = new NioSocketAcceptor();
+
+        socketAcceptor.getFilterChain().addFirst("ssl", filterServer);
+        CompareFilter filter = new CompareFilter();
+        socketAcceptor.setHandler(filter);
+
+        try {
+            socketAcceptor.bind(new InetSocketAddress("localhost", 0));
+
+            final IoFuture connectFuture = 
socketConnector.connect(socketAcceptor.getLocalAddress());
+            connectFuture.awaitUninterruptibly();
+
+            final IoSession clientSocket = connectFuture.getSession();
+            try {
+                SSLEngine sslClient = filterClient.getEngine();
+                LOG.error("client packet size " + 
sslClient.getSession().getPacketBufferSize());
+                LOG.error("client app buffer size " + 
sslClient.getSession().getApplicationBufferSize());
+                for (int msgSize = 61000; msgSize < 80000; msgSize++) {
+                    LOG.error("Trying " + msgSize);
+                    CountDownLatch received = new CountDownLatch(1);
+                    filter.init(received, msgSize);
+                    IoBuffer data = createMosaicRequest(msgSize);
+                    clientSocket.write(data).await();
+                    data.free();
+                    assertTrue(received.await(5, TimeUnit.SECONDS));
+                    assertNoException(filter.getFailure());
+                }
+            } finally {
+                clientSocket.closeNow().await();
+            }
+        } finally {
+            socketAcceptor.unbind();
+            socketAcceptor.dispose();
+            socketConnector.dispose();
+        }
+    }
+
+    private static void assertNoException(Throwable t) {
+        if (t != null) {
+            throw new AssertionError(t);
+        }
+    }
+
+    private static class DebugFilter extends IoHandlerAdapter {
+
+        @Override
+        public void messageReceived(IoSession session, Object message) throws 
Exception {
+            IoBuffer b = IoBuffer.class.cast(message);
+            LOG.error("Client: received clear-text message\n" + 
b.getHexDump(true));
+        }
+    }
+
+    private static class CompareFilter extends IoHandlerAdapter {
+
+        private CountDownLatch done;
+
+        private int needBytes;
+
+        private IoBuffer acc;
+
+        private Throwable failure;
+
+        CompareFilter() {
+            super();
+        }
+
+        void init(CountDownLatch done, int size) {
+            if (this.acc != null) {
+                this.acc.free();
+            }
+            this.done = done;
+            this.needBytes = size;
+            this.acc = IoBuffer.allocate(size);
+        }
+
+        Throwable getFailure() {
+            return failure;
+        }
+
+        @Override
+        public void exceptionCaught(IoSession session, Throwable cause) throws 
Exception {
+            LOG.error("Remote: got exception", cause);
+            failure = cause;
+            done.countDown();
+        }
+
+        @Override
+        public void messageReceived(IoSession session, Object message) throws 
Exception {
+            if (needBytes <= 0) {
+                LOG.error("Remote: got spurious data");
+                return;
+            }
+            IoBuffer b = IoBuffer.class.cast(message);
+            int received = b.remaining();
+            if (received > needBytes) {
+                LOG.error("Remote: got " + received + " bytes, expected only " 
+ needBytes);
+                byte[] data = new byte[needBytes];
+                b.get(data);
+                acc.put(data);
+            } else {
+                LOG.error("Remote: got " + received + " bytes");
+                acc.put(b);
+            }
+            needBytes -= received;
+            LOG.error("Remote: still need " + needBytes + " bytes");
+            if (needBytes <= 0) {
+                int i = 0;
+                int j = 0;
+                acc.flip();
+                while (acc.hasRemaining()) {
+                    int val = acc.get() & 0xFF;
+                    int cmp = i % 16367 == 0 ? j++ : 0x11;
+                    if (val != cmp) {
+                        LOG.error("Remote: data mismatch at index " + i);
+                        break;
+                    }
+                    i++;
+                    if (acc.hasRemaining()) {
+                        val = acc.get() & 0xFF;
+                        cmp = i % 16367 == 0 ? j++ : 0xAA;
+                        if (val != cmp) {
+                            LOG.error("Remote: data mismatch at index " + i);
+                            break;
+                        }
+                        i++;
+                    }
+                }
+                done.countDown();
+            }
+        }
+    }
+
+    public static IoBuffer createMosaicRequest(int size) {
+        IoBuffer message = IoBuffer.allocate(size);
+        int i = 0;
+        int j = 0;
+        while (message.hasRemaining()) {
+            if (i++ % 16367 == 0) {
+                message.put((byte) j);
+                j++;
+            } else {
+                message.put((byte) 0x11);
+            }
+            if (message.hasRemaining()) {
+                if (i++ % 16367 == 0) {
+                    message.put((byte) j);
+                    j++;
+                } else {
+                    message.put((byte) 0xAA);
+                }
+            }
+        }
+        message.flip();
+        return message;
+    }
+
+    private static class MySslFilter extends SslFilter {
+
+        MySslFilter(SSLContext sslContext) {
+            super(sslContext);
+        }
+
+        private SSLEngine ssl;
+
+        @Override
+        protected SSLEngine createEngine(IoSession session, InetSocketAddress 
addr) {
+            ssl = super.createEngine(session, addr);
+            return ssl;
+        }
+
+        SSLEngine getEngine() {
+            return ssl;
+        }
+    }
+}

Reply via email to