You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by je...@apache.org on 2013/06/05 22:40:37 UTC

[1/3] git commit: The readBuffer is now only cleared for not secured sessions

Updated Branches:
  refs/heads/trunk dbbdf9ea0 -> 31767ad4c


The readBuffer is now only cleared for not secured sessions

Project: http://git-wip-us.apache.org/repos/asf/mina/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina/commit/8bc3c404
Tree: http://git-wip-us.apache.org/repos/asf/mina/tree/8bc3c404
Diff: http://git-wip-us.apache.org/repos/asf/mina/diff/8bc3c404

Branch: refs/heads/trunk
Commit: 8bc3c4047e2af669c6c8cbdc4d05ad8c63e914ef
Parents: 2a1dbba
Author: Emmanuel Lécharny <el...@apache.org>
Authored: Fri May 3 16:05:24 2013 +0200
Committer: Emmanuel Lécharny <el...@apache.org>
Committed: Fri May 3 16:05:24 2013 +0200

----------------------------------------------------------------------
 .../mina/transport/nio/tcp/NioTcpSession.java      |    8 +++++---
 1 files changed, 5 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina/blob/8bc3c404/core/src/main/java/org/apache/mina/transport/nio/tcp/NioTcpSession.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/mina/transport/nio/tcp/NioTcpSession.java b/core/src/main/java/org/apache/mina/transport/nio/tcp/NioTcpSession.java
index f06fda7..cd9e8d7 100644
--- a/core/src/main/java/org/apache/mina/transport/nio/tcp/NioTcpSession.java
+++ b/core/src/main/java/org/apache/mina/transport/nio/tcp/NioTcpSession.java
@@ -290,9 +290,6 @@ public class NioTcpSession extends AbstractIoSession implements SelectorListener
         try {
             LOG.debug("readable session : {}", this);
 
-            // First reset the buffer from what it contained before
-            readBuffer.clear();
-
             // Read everything we can up to the buffer size
             final int readCount = ((SocketChannel) channel).read(readBuffer);
 
@@ -318,9 +315,14 @@ public class NioTcpSession extends AbstractIoSession implements SelectorListener
                     }
 
                     sslHelper.processRead(this, readBuffer);
+
+                    // We don't clear the buffer. It has been done by the sslHelper
                 } else {
                     // Plain message, not encrypted : go directly to the chain
                     processMessageReceived(readBuffer);
+
+                    // And now, clear the buffer
+                    readBuffer.clear();
                 }
 
                 // Update the session idle status


[3/3] git commit: Merge branch 'ssl-experiment' into trunk

Posted by je...@apache.org.
Merge branch 'ssl-experiment' into trunk


Project: http://git-wip-us.apache.org/repos/asf/mina/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina/commit/31767ad4
Tree: http://git-wip-us.apache.org/repos/asf/mina/tree/31767ad4
Diff: http://git-wip-us.apache.org/repos/asf/mina/diff/31767ad4

Branch: refs/heads/trunk
Commit: 31767ad4ce30e71c981865598c9f8764b7e5d3f2
Parents: dbbdf9e 2859673
Author: Jeff MAURY <je...@apache.org>
Authored: Wed Jun 5 22:39:49 2013 +0200
Committer: Jeff MAURY <je...@apache.org>
Committed: Wed Jun 5 22:39:49 2013 +0200

----------------------------------------------------------------------
 .../org/apache/mina/transport/nio/SslHelper.java   |  300 +++++----------
 .../org/apache/mina/transport/nio/SslTest.java     |    5 +-
 .../org/apache/mina/transport/nio/keystore.cert    |  Bin 0 -> 937 bytes
 .../org/apache/mina/transport/nio/keystore.sslTest |  Bin 0 -> 1368 bytes
 .../apache/mina/transport/nio/truststore.sslTest   |  Bin 0 -> 654 bytes
 .../org/apache/mina/transport/tcp/keystore.cert    |  Bin 937 -> 0 bytes
 .../org/apache/mina/transport/tcp/keystore.sslTest |  Bin 1368 -> 0 bytes
 .../apache/mina/transport/tcp/truststore.sslTest   |  Bin 654 -> 0 bytes
 8 files changed, 103 insertions(+), 202 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina/blob/31767ad4/core/src/main/java/org/apache/mina/transport/nio/SslHelper.java
----------------------------------------------------------------------
diff --cc core/src/main/java/org/apache/mina/transport/nio/SslHelper.java
index d6e063f,0000000..c2996d2
mode 100644,000000..100644
--- a/core/src/main/java/org/apache/mina/transport/nio/SslHelper.java
+++ b/core/src/main/java/org/apache/mina/transport/nio/SslHelper.java
@@@ -1,421 -1,0 +1,323 @@@
 +/*
 + *  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.transport.nio;
 +
 +import static org.apache.mina.session.AttributeKey.createKey;
 +
 +import java.net.InetSocketAddress;
 +import java.nio.ByteBuffer;
 +import java.util.Queue;
 +
 +import javax.net.ssl.SSLContext;
 +import javax.net.ssl.SSLEngine;
 +import javax.net.ssl.SSLEngineResult;
 +import javax.net.ssl.SSLEngineResult.HandshakeStatus;
- import javax.net.ssl.SSLEngineResult.Status;
 +import javax.net.ssl.SSLException;
 +import javax.net.ssl.SSLSession;
 +
 +import org.apache.mina.api.IoClient;
 +import org.apache.mina.api.IoSession;
 +import org.apache.mina.api.IoSession.SessionState;
 +import org.apache.mina.session.AbstractIoSession;
 +import org.apache.mina.session.AttributeKey;
 +import org.apache.mina.session.DefaultWriteRequest;
 +import org.apache.mina.session.WriteRequest;
 +import org.slf4j.Logger;
 +import org.slf4j.LoggerFactory;
 +
 +/**
 + * An helper class used to manage everything related to SSL/TLS establishment and management.
 + * 
 + * @author <a href="http://mina.apache.org">Apache MINA Project</a>
 + */
 +public class SslHelper {
 +    /** A logger for this class */
 +    private static final Logger LOGGER = LoggerFactory.getLogger(SslHelper.class);
 +
 +    /** The SSL engine instance */
 +    private SSLEngine sslEngine;
 +
 +    /** The SSLContext instance */
 +    private final SSLContext sslContext;
 +
 +    /** The current session */
 +    private final IoSession session;
 +
 +    /**
 +     * A session attribute key that should be set to an {@link InetSocketAddress}. Setting this attribute causes
 +     * {@link SSLContext#createSSLEngine(String, int)} to be called passing the hostname and port of the
 +     * {@link InetSocketAddress} to get an {@link SSLEngine} instance. If not set {@link SSLContext#createSSLEngine()}
 +     * will be called.<br/>
 +     * Using this feature {@link SSLSession} objects may be cached and reused when in client mode.
 +     * 
 +     * @see SSLContext#createSSLEngine(String, int)
 +     */
 +    public static final AttributeKey<InetSocketAddress> PEER_ADDRESS = createKey(InetSocketAddress.class,
 +            "internal_peerAddress");
 +
 +    public static final AttributeKey<Boolean> WANT_CLIENT_AUTH = createKey(Boolean.class, "internal_wantClientAuth");
 +
 +    public static final AttributeKey<Boolean> NEED_CLIENT_AUTH = createKey(Boolean.class, "internal_needClientAuth");
 +
 +    /** Incoming buffer accumulating bytes read from the channel */
 +    /** An empty buffer used during the handshake phase */
 +    private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0);
 +
 +    /** An empty buffer used during the handshake phase */
 +    private static final ByteBuffer HANDSHAKE_BUFFER = ByteBuffer.allocate(1024);
++    
++    private ByteBuffer previous = null;
 +
 +    /**
 +     * Create a new SSL Handler.
 +     * 
 +     * @param session The associated session
 +     */
 +    public SslHelper(IoSession session, SSLContext sslContext) {
 +        this.session = session;
 +        this.sslContext = sslContext;
 +    }
 +
 +    /**
 +     * @return The associated session
 +     */
 +    /* no qualifier */IoSession getSession() {
 +        return session;
 +    }
 +
 +    /**
 +     * @return The associated SSLEngine
 +     */
 +    /* no qualifier */SSLEngine getEngine() {
 +        return sslEngine;
 +    }
 +
++    boolean isHanshaking() {
++        return sslEngine.getHandshakeStatus() != HandshakeStatus.NOT_HANDSHAKING;
++    }
++    
 +    /**
 +     * Initialize the SSL handshake.
 +     * 
 +     * @throws SSLException If the underlying SSLEngine handshake initialization failed
 +     */
 +    public void init() throws SSLException {
 +        if (sslEngine != null) {
 +            // We already have a SSL engine created, no need to create a new one
 +            return;
 +        }
 +
 +        LOGGER.debug("{} Initializing the SSL Helper", session);
 +
 +        InetSocketAddress peer = session.getAttribute(PEER_ADDRESS, null);
 +
 +        // Create the SSL engine here
 +        if (peer == null) {
 +            sslEngine = sslContext.createSSLEngine();
 +        } else {
 +            sslEngine = sslContext.createSSLEngine(peer.getHostName(), peer.getPort());
 +        }
 +
 +        // Initialize the engine in client mode if necessary
 +        sslEngine.setUseClientMode(session.getService() instanceof IoClient);
 +
 +        // Initialize the different SslEngine modes
 +        if (!sslEngine.getUseClientMode()) {
 +            // Those parameters are only valid when in server mode
 +            boolean needClientAuth = session.getAttribute(NEED_CLIENT_AUTH, false);
 +            boolean wantClientAuth = session.getAttribute(WANT_CLIENT_AUTH, false);
 +
 +            // The WantClientAuth supersede the NeedClientAuth, if set.
 +            if (needClientAuth) {
 +                sslEngine.setNeedClientAuth(true);
 +            }
 +
 +            if (wantClientAuth) {
 +                sslEngine.setWantClientAuth(true);
 +            }
 +        }
 +
 +        if (LOGGER.isDebugEnabled()) {
 +            LOGGER.debug("{} SSL Handler Initialization done.", session);
 +        }
 +    }
 +
 +    /**
-      * Process the NEED_TASK action.
++     * Duplicate a byte buffer for storing it into this context for future
++     * use.
 +     * 
-      * @param engine The SSLEngine instance
-      * @return The resulting HandshakeStatus
-      * @throws SSLException If we've got an error while processing the tasks
++     * @param buffer the buffer to duplicate
++     * @return the newly allocated buffer
 +     */
-     private HandshakeStatus processTasks(SSLEngine engine) throws SSLException {
-         Runnable runnable;
- 
-         while ((runnable = engine.getDelegatedTask()) != null) {
-             // TODO : we may have to use a thread pool here to improve the
-             // performances
-             runnable.run();
-         }
- 
-         HandshakeStatus hsStatus = engine.getHandshakeStatus();
- 
-         return hsStatus;
++    private ByteBuffer duplicate(ByteBuffer buffer) {
++        ByteBuffer newBuffer = ByteBuffer.allocateDirect(buffer.remaining() * 2);
++        newBuffer.put(buffer);
++        newBuffer.flip();
++        return newBuffer;
 +    }
- 
++    
 +    /**
-      * Process the NEED_UNWRAP action. We have to read the incoming buffer, and to feed the application buffer.
++     * Accumulate the given buffer into the current context. Allocation is performed only
++     * if needed.
++     * 
++     * @param buffer the buffer to accumulate
++     * @return the accumulated buffer
 +     */
-     private SSLEngineResult unwrap(ByteBuffer inBuffer, ByteBuffer appBuffer) throws SSLException {
-         // First work with either the new incoming buffer, or the accumulating buffer
-         ByteBuffer tempBuffer = inBuffer;
- 
-         // Loop until we have processed the entire incoming buffer,
-         // or until we have to stop
-         while (true) {
-             // Do the unwrapping
-             SSLEngineResult result = sslEngine.unwrap(tempBuffer, appBuffer);
- 
-             switch (result.getStatus()) {
-             case OK:
-                 // Ok, we have unwrapped a message, return.
-                 return result;
- 
-             case BUFFER_UNDERFLOW:
-                 // We need to read some more data from the channel.
- 
-                 inBuffer.clear();
- 
-                 return result;
- 
-             case CLOSED:
-                 // We have received a Close message, we can exit now
-                 if (session.isConnectedSecured()) {
-                     return result;
-                 } else {
-                     throw new IllegalStateException();
-                 }
- 
-             case BUFFER_OVERFLOW:
-                 // We have to increase the appBuffer size. In any case
-                 // we aren't processing an handshake here. Read again.
-                 appBuffer = ByteBuffer.allocate(appBuffer.capacity() + 4096);
-             }
++    private ByteBuffer accumulate(ByteBuffer buffer) {
++        if (previous.capacity() - previous.remaining() > buffer.remaining()) {
++            int oldPosition = previous.position();
++            previous.position(previous.limit());
++            previous.limit(previous.limit() + buffer.remaining());
++            previous.put(buffer);
++            previous.position(oldPosition);
++        } else {
++            ByteBuffer newPrevious = ByteBuffer.allocateDirect((previous.remaining() + buffer.remaining() ) * 2);
++            newPrevious.put(previous);
++            newPrevious.put(buffer);
++            newPrevious.flip();
++            previous = newPrevious;
 +        }
++        return previous;
 +    }
- 
++    
 +    /**
 +     * Process a read ByteBuffer over a secured connection, or during the SSL/TLS Handshake.
 +     * 
 +     * @param session The session we are processing a read for
 +     * @param readBuffer The data we get from the channel
 +     * @throws SSLException If the unwrapping or handshaking failed
 +     */
 +    public void processRead(AbstractIoSession session, ByteBuffer readBuffer) throws SSLException {
-         if (session.isConnectedSecured()) {
-             // Unwrap the incoming data
-             while (readBuffer.hasRemaining()) {
-                 processUnwrap(session, readBuffer);
-             }
++        ByteBuffer tempBuffer;
++        
++        if (previous != null) {
++            tempBuffer = accumulate(readBuffer);
 +        } else {
-             // Process the SSL handshake now
-             processHandShake(session, readBuffer);
-         }
-     }
- 
-     /**
-      * Unwrap a SSL/TLS message. The message might not be encrypted (if we are processing a Handshake message or an
-      * Alert message).
-      */
-     private void processUnwrap(AbstractIoSession session, ByteBuffer inBuffer) throws SSLException {
-         // Blind guess : once uncompressed, the resulting buffer will be 3 times bigger
-         ByteBuffer appBuffer = ByteBuffer.allocate(inBuffer.limit() * 3);
-         SSLEngineResult result = unwrap(inBuffer, appBuffer);
- 
-         switch (result.getStatus()) {
-         case OK:
-             // Ok, go through the chain now
-             appBuffer.flip();
-             session.processMessageReceived(appBuffer);
-             break;
- 
-         case CLOSED:
-             // This was a Alert Closure message. Process it
-             processClosed(result);
- 
-             break;
-         }
-     }
- 
-     /**
-      * Process the SSL/TLS Alert Closure message
-      */
-     private void processClosed(SSLEngineResult result) throws SSLException {
-         // We have received a Alert_CLosure message, we will have to do a wrap
-         HandshakeStatus hsStatus = result.getHandshakeStatus();
- 
-         if (hsStatus == HandshakeStatus.NEED_WRAP) {
-             // We need to send back the Alert Closure message
-             if (LOGGER.isDebugEnabled()) {
-                 LOGGER.debug("{} processing the NEED_WRAP state", session);
-             }
- 
-             int capacity = sslEngine.getSession().getPacketBufferSize();
-             ByteBuffer outBuffer = ByteBuffer.allocate(capacity);
-             session.changeState(SessionState.CONNECTED);
- 
-             // Loop until the SSLEngine has nothing more to produce
-             while (!sslEngine.isOutboundDone()) {
-                 sslEngine.wrap(EMPTY_BUFFER, outBuffer);
-                 outBuffer.flip();
- 
-                 // Get out of the Connected state
-                 WriteRequest writeRequest = new DefaultWriteRequest(outBuffer);
-                 session.enqueueWriteRequest(writeRequest);
-             }
-         }
-         session.close(false);
-     }
- 
-     /**
-      * Process the SLL/TLS Handshake. We may enter in this method more than once, as the handshake is a dialogue between
-      * the client and the server.
-      */
-     private void processHandShake(IoSession session, ByteBuffer inBuffer) throws SSLException {
-         // Start the Handshake if we aren't already processing a HandShake
-         // and switch to the SECURING state
-         HandshakeStatus hsStatus = sslEngine.getHandshakeStatus();
- 
-         // Initilize the session status when we enter into the Handshake process.
-         // Not that we don't call the SSLEngine.beginHandshake() method :
-         // It's implicitely done internally by the unwrap() method.
-         if (hsStatus == HandshakeStatus.NOT_HANDSHAKING) {
-             session.changeState(SessionState.SECURING);
++            tempBuffer = readBuffer;
 +        }
- 
-         SSLEngineResult result = null;
- 
-         // If the SSLEngine has not be started, then the status will be NOT_HANDSHAKING
-         // We loop until we reach the FINISHED state
-         while (hsStatus != HandshakeStatus.FINISHED) {
-             if (hsStatus == HandshakeStatus.NEED_TASK) {
-                 hsStatus = processTasks(sslEngine);
-             } else if (hsStatus == HandshakeStatus.NEED_WRAP) {
-                 if (LOGGER.isDebugEnabled()) {
-                     LOGGER.debug("{} processing the NEED_WRAP state", session);
++        
++        
++        boolean done = false;
++        SSLEngineResult result;
++        ByteBuffer appBuffer = ByteBuffer.allocateDirect(sslEngine.getSession().getApplicationBufferSize());
++        
++        HandshakeStatus handshakeStatus = sslEngine.getHandshakeStatus();
++        while (!done) {
++            switch (handshakeStatus) {
++            case NEED_UNWRAP:
++            case NOT_HANDSHAKING:
++            case FINISHED:
++              result = sslEngine.unwrap(tempBuffer, appBuffer);
++              handshakeStatus = result.getHandshakeStatus();
++
++              switch (result.getStatus()) {
++              case BUFFER_UNDERFLOW:
++                  /* we need more data */
++                  done = true;
++                  break;
++              case BUFFER_OVERFLOW:
++                  /* resize output buffer */
++                  appBuffer = ByteBuffer.allocateDirect(appBuffer.capacity() * 2);
++                  break;
++              case OK:
++                  if ((handshakeStatus == HandshakeStatus.NOT_HANDSHAKING) &&
++                      (result.bytesProduced() > 0)) {
++                      appBuffer.flip();
++                      session.processMessageReceived(appBuffer);
++                  }
++              }
++              break;
++            case NEED_TASK:
++                Runnable task;
++                
++                while ((task = sslEngine.getDelegatedTask()) != null) {
++                    task.run();
 +                }
- 
-                 // Create an insanely wide buffer, as the SSLEngine requires it
-                 int capacity = sslEngine.getSession().getPacketBufferSize();
-                 ByteBuffer outBuffer = ByteBuffer.allocate(capacity);
- 
-                 boolean completed = false;
- 
-                 // Loop until we are able to wrap the message (we may have
-                 // to increase the buffer size more than once.
-                 while (!completed) {
-                     result = sslEngine.wrap(EMPTY_BUFFER, outBuffer);
- 
-                     switch (result.getStatus()) {
-                     case OK:
-                     case CLOSED:
-                         completed = true;
-                         break;
- 
-                     case BUFFER_OVERFLOW:
-                         // Increase the target buffer size
-                         outBuffer = ByteBuffer.allocate(outBuffer.capacity() + 4096);
-                         break;
-                     }
-                 }
- 
-                 // Done. We can now push this buffer into the write queue.
-                 outBuffer.flip();
-                 WriteRequest writeRequest = new DefaultWriteRequest(inBuffer);
-                 writeRequest.setMessage(outBuffer);
-                 session.enqueueWriteRequest(writeRequest);
-                 hsStatus = result.getHandshakeStatus();
- 
-                 // Nothing more to wrap : get out.
-                 // Note to self : we can probably use only one ByteBuffer for the
-                 // multiple wrapped messages. (see https://issues.apache.org/jira/browse/DIRMINA-878)
-                 if (hsStatus != HandshakeStatus.NEED_WRAP) {
++                handshakeStatus = sslEngine.getHandshakeStatus();
++                break;
++            case NEED_WRAP:
++                result = sslEngine.wrap(EMPTY_BUFFER, appBuffer);
++                handshakeStatus = result.getHandshakeStatus();
++                switch (result.getStatus()) {
++                case  BUFFER_OVERFLOW:
++                    appBuffer = ByteBuffer.allocateDirect(appBuffer.capacity() * 2);
 +                    break;
-                 }
-             } else if ((hsStatus == HandshakeStatus.NEED_UNWRAP) || (hsStatus == HandshakeStatus.NOT_HANDSHAKING)) {
-                 // We cover the ongoing handshake (NEED_UNWRAP) and
-                 // the initial call to the handshake (NOT_HANDSHAKING)
-                 result = unwrap(inBuffer, HANDSHAKE_BUFFER);
- 
-                 if (result.getStatus() == Status.BUFFER_UNDERFLOW) {
-                     // Read more data
++                case BUFFER_UNDERFLOW:
++                    done = true;
++                    break;
++                case CLOSED:
++                case OK:
++                    appBuffer.flip();
++                    WriteRequest writeRequest = new DefaultWriteRequest(readBuffer);
++                    writeRequest.setMessage(appBuffer);
++                    session.enqueueWriteRequest(writeRequest);
 +                    break;
-                 } else {
-                     hsStatus = result.getHandshakeStatus();
 +                }
 +            }
 +        }
- 
-         if (hsStatus == HandshakeStatus.FINISHED) {
-             // The handshake has been completed. We can change the session's state.
-             if (LOGGER.isDebugEnabled()) {
-                 LOGGER.debug("{} processing the FINISHED state", session);
-             }
- 
-             session.changeState(SessionState.SECURED);
++        if (tempBuffer.remaining() > 0) {
++            previous = duplicate(tempBuffer);
++        } else {
++            previous = null;
 +        }
-     }
++        readBuffer.clear();
++     }
 +
 +    /**
 +     * Process the application data encryption for a session.
 +     * 
 +     * @param session The session sending encrypted data to the peer.
 +     * @param message The message to encrypt
 +     * @param writeQueue The queue in which the encrypted buffer will be written
 +     * @return The written WriteRequest
 +     */
 +    /** No qualifier */
 +    WriteRequest processWrite(IoSession session, Object message, Queue<WriteRequest> writeQueue) {
 +        ByteBuffer buf = (ByteBuffer) message;
 +        ByteBuffer appBuffer = ByteBuffer.allocate(sslEngine.getSession().getPacketBufferSize());
 +
 +        try {
 +            while (true) {
 +                // Encrypt the message
 +                SSLEngineResult result = sslEngine.wrap(buf, appBuffer);
 +
 +                switch (result.getStatus()) {
 +                case BUFFER_OVERFLOW:
 +                    // Increase the buffer size as needed
 +                    appBuffer = ByteBuffer.allocate(appBuffer.capacity() + 4096);
 +                    break;
 +
 +                case BUFFER_UNDERFLOW:
 +                case CLOSED:
 +                    break;
 +
 +                case OK:
 +                    // We are done. Flip the buffer and push it to the write queue.
 +                    appBuffer.flip();
 +                    WriteRequest request = new DefaultWriteRequest(appBuffer);
 +
 +                    return request;
 +                }
 +            }
 +        } catch (SSLException se) {
 +            throw new IllegalStateException(se.getMessage());
 +        }
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/mina/blob/31767ad4/core/src/test/java/org/apache/mina/transport/nio/SslTest.java
----------------------------------------------------------------------
diff --cc core/src/test/java/org/apache/mina/transport/nio/SslTest.java
index 019fb47,0000000..fad8428
mode 100644,000000..100644
--- a/core/src/test/java/org/apache/mina/transport/nio/SslTest.java
+++ b/core/src/test/java/org/apache/mina/transport/nio/SslTest.java
@@@ -1,215 -1,0 +1,214 @@@
 +/*
 + *  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.transport.nio;
 +
 +import static org.junit.Assert.assertTrue;
 +
 +import java.io.BufferedReader;
 +import java.io.IOException;
 +import java.io.InputStreamReader;
 +import java.net.InetAddress;
 +import java.net.InetSocketAddress;
 +import java.net.Socket;
 +import java.nio.ByteBuffer;
 +import java.nio.charset.Charset;
 +import java.security.GeneralSecurityException;
 +import java.security.KeyStore;
 +import java.security.Security;
 +import java.util.concurrent.CountDownLatch;
 +import java.util.concurrent.TimeUnit;
 +
 +import javax.net.ssl.KeyManagerFactory;
 +import javax.net.ssl.SSLContext;
 +import javax.net.ssl.SSLSocketFactory;
 +import javax.net.ssl.TrustManagerFactory;
 +
 +import org.apache.mina.api.AbstractIoHandler;
 +import org.apache.mina.api.IoSession;
 +import org.apache.mina.transport.nio.NioTcpServer;
 +import org.junit.Ignore;
 +import org.junit.Test;
 +
 +/**
 + * Test a SSL session where the connection is established and closed twice. It should be
 + * processed correctly (Test for DIRMINA-650)
 + *
 + * @author <a href="http://mina.apache.org">Apache MINA Project</a>
 + */
 +public class SslTest {
 +    private static Exception clientError = null;
 +
 +    private static InetAddress address;
 +
 +    private static SSLSocketFactory factory;
 +
 +    /** A JVM independant KEY_MANAGER_FACTORY algorithm */
 +    private static final String KEY_MANAGER_FACTORY_ALGORITHM;
 +
 +    static {
 +        String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm");
 +        if (algorithm == null) {
 +            algorithm = KeyManagerFactory.getDefaultAlgorithm();
 +        }
 +
 +        KEY_MANAGER_FACTORY_ALGORITHM = algorithm;
 +    }
 +
 +    private static class TestHandler extends AbstractIoHandler {
 +        public void messageReceived(IoSession session, Object message) {
 +            String line = Charset.defaultCharset().decode((ByteBuffer) message).toString();
 +
 +            if (line.startsWith("hello")) {
 +                System.out.println("Server got: 'hello', waiting for 'send'");
 +            } else if (line.startsWith("send")) {
 +                System.out.println("Server got: 'send', sending 'data'");
 +                session.write(Charset.defaultCharset().encode("data\n"));
 +            }
 +        }
 +    }
 +
 +    /**
 +     * Starts a Server with the SSL Filter and a simple text line 
 +     * protocol codec filter
 +     */
 +    private static int startServer() throws Exception {
 +        NioTcpServer server = new NioTcpServer();
 +
 +        server.setReuseAddress(true);
 +        server.getSessionConfig().setSslContext(createSSLContext());
 +        server.setIoHandler(new TestHandler());
 +        server.bind(new InetSocketAddress(0));
 +        return server.getServerSocketChannel().socket().getLocalPort();
 +    }
 +
 +    /**
 +     * Starts a client which will connect twice using SSL
 +     */
 +    private static void startClient(int port) throws Exception {
 +        address = InetAddress.getByName("localhost");
 +
 +        SSLContext context = createSSLContext();
 +        factory = context.getSocketFactory();
 +
 +        connectAndSend(port);
 +
 +        // This one will throw a SocketTimeoutException if DIRMINA-650 is not fixed
 +        connectAndSend(port);
 +    }
 +
 +    private static void connectAndSend(int port) throws Exception {
 +        Socket parent = new Socket(address, port);
 +        Socket socket = factory.createSocket(parent, address.getCanonicalHostName(), port, false);
 +
 +        System.out.println("Client sending: hello");
 +        socket.getOutputStream().write("hello                      \n".getBytes());
 +        socket.getOutputStream().flush();
 +        socket.setSoTimeout(10000);
 +
 +        System.out.println("Client sending: send");
 +        socket.getOutputStream().write("send\n".getBytes());
 +        socket.getOutputStream().flush();
 +
 +        BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
 +        String line = in.readLine();
 +        System.out.println("Client got: " + line);
 +        socket.close();
 +
 +    }
 +
 +    private static SSLContext createSSLContext() throws IOException, GeneralSecurityException {
 +        char[] passphrase = "password".toCharArray();
 +
 +        SSLContext ctx = SSLContext.getInstance("TLS");
 +        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KEY_MANAGER_FACTORY_ALGORITHM);
 +        TrustManagerFactory tmf = TrustManagerFactory.getInstance(KEY_MANAGER_FACTORY_ALGORITHM);
 +
 +        KeyStore ks = KeyStore.getInstance("JKS");
 +        KeyStore ts = KeyStore.getInstance("JKS");
 +
 +        ks.load(SslTest.class.getResourceAsStream("keystore.sslTest"), passphrase);
 +        ts.load(SslTest.class.getResourceAsStream("truststore.sslTest"), passphrase);
 +
 +        kmf.init(ks, passphrase);
 +        tmf.init(ts);
 +        ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
 +
 +        return ctx;
 +    }
 +
 +    @Test
-     @Ignore("SslHelper needs more attention for big messages")
++    @Ignore("check for fragmentation")
 +    public void testSSL() throws Exception {
 +        final int port = startServer();
 +
 +        Thread t = new Thread() {
 +            public void run() {
 +                try {
 +                    startClient(port);
 +                } catch (Exception e) {
 +                    clientError = e;
 +                }
 +            }
 +        };
 +        t.start();
 +        t.join();
 +        if (clientError != null)
 +            throw clientError;
 +    }
 +
 +    @Test
-     @Ignore("SslHelper needs more attention for big messages")
 +    public void testBigMessage() throws IOException, GeneralSecurityException, InterruptedException {
 +        final CountDownLatch counter = new CountDownLatch(1);
 +        NioTcpServer server = new NioTcpServer();
 +        final int messageSize = 1 * 1024 * 1024;
 +
 +        /*
 +         * Server
 +         */
 +        server.setReuseAddress(true);
 +        server.getSessionConfig().setSslContext(createSSLContext());
 +        server.setIoHandler(new AbstractIoHandler() {
 +            private int receivedSize = 0;
 +
 +            /**
 +             * {@inheritedDoc}
 +             */
 +            @Override
 +            public void messageReceived(IoSession session, Object message) {
 +                receivedSize += ((ByteBuffer) message).remaining();
-                 if (receivedSize == 0) {
++                if (receivedSize == messageSize) {
 +                    counter.countDown();
 +                }
 +            }
 +        });
 +        server.bind(new InetSocketAddress(0));
 +        int port = server.getServerSocketChannel().socket().getLocalPort();
 +
 +        /*
 +         * Client
 +         */
 +        Socket socket = server.getSessionConfig().getSslContext().getSocketFactory().createSocket("localhost", port);
 +        socket.getOutputStream().write(new byte[messageSize]);
 +        socket.getOutputStream().flush();
 +        socket.close();
 +        assertTrue(counter.await(10, TimeUnit.SECONDS));
 +
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/mina/blob/31767ad4/core/src/test/resources/org/apache/mina/transport/nio/keystore.cert
----------------------------------------------------------------------
diff --cc core/src/test/resources/org/apache/mina/transport/nio/keystore.cert
index 0000000,0000000..d34502d
new file mode 100644
Binary files differ

http://git-wip-us.apache.org/repos/asf/mina/blob/31767ad4/core/src/test/resources/org/apache/mina/transport/nio/keystore.sslTest
----------------------------------------------------------------------
diff --cc core/src/test/resources/org/apache/mina/transport/nio/keystore.sslTest
index 0000000,0000000..36190ba
new file mode 100644
Binary files differ

http://git-wip-us.apache.org/repos/asf/mina/blob/31767ad4/core/src/test/resources/org/apache/mina/transport/nio/truststore.sslTest
----------------------------------------------------------------------
diff --cc core/src/test/resources/org/apache/mina/transport/nio/truststore.sslTest
index 0000000,0000000..48c5963
new file mode 100644
Binary files differ

http://git-wip-us.apache.org/repos/asf/mina/blob/31767ad4/core/src/test/resources/org/apache/mina/transport/tcp/keystore.cert
----------------------------------------------------------------------
diff --cc core/src/test/resources/org/apache/mina/transport/tcp/keystore.cert
index d34502d,d34502d..0000000
deleted file mode 100644,100644
Binary files differ

http://git-wip-us.apache.org/repos/asf/mina/blob/31767ad4/core/src/test/resources/org/apache/mina/transport/tcp/keystore.sslTest
----------------------------------------------------------------------
diff --cc core/src/test/resources/org/apache/mina/transport/tcp/keystore.sslTest
index 36190ba,36190ba..0000000
deleted file mode 100644,100644
Binary files differ

http://git-wip-us.apache.org/repos/asf/mina/blob/31767ad4/core/src/test/resources/org/apache/mina/transport/tcp/truststore.sslTest
----------------------------------------------------------------------
diff --cc core/src/test/resources/org/apache/mina/transport/tcp/truststore.sslTest
index 48c5963,48c5963..0000000
deleted file mode 100644,100644
Binary files differ


[2/3] git commit: Rewrote the SslHelper to be handshake status driven. Need attention to buffer management. Supports rehandshaking but needs attention to previous and future queued messages. Consider as a first attempt only

Posted by je...@apache.org.
Rewrote the SslHelper to be handshake status driven.
Need attention to buffer management.
Supports rehandshaking but needs attention to previous and future queued messages.
Consider as a first attempt only


Project: http://git-wip-us.apache.org/repos/asf/mina/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina/commit/2859673d
Tree: http://git-wip-us.apache.org/repos/asf/mina/tree/2859673d
Diff: http://git-wip-us.apache.org/repos/asf/mina/diff/2859673d

Branch: refs/heads/trunk
Commit: 2859673da149e1a23bc9c1c7e187bf1dd61fe904
Parents: 8bc3c40
Author: Jeff MAURY <je...@apache.org>
Authored: Sun May 5 22:33:33 2013 +0200
Committer: Jeff MAURY <je...@apache.org>
Committed: Sun May 5 22:33:33 2013 +0200

----------------------------------------------------------------------
 .../java/org/apache/mina/session/SslHelper.java    |  302 +++++----------
 .../org/apache/mina/transport/tcp/SslTest.java     |    5 +-
 2 files changed, 103 insertions(+), 204 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mina/blob/2859673d/core/src/main/java/org/apache/mina/session/SslHelper.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/mina/session/SslHelper.java b/core/src/main/java/org/apache/mina/session/SslHelper.java
index 80af05c..74843a8 100644
--- a/core/src/main/java/org/apache/mina/session/SslHelper.java
+++ b/core/src/main/java/org/apache/mina/session/SslHelper.java
@@ -29,13 +29,11 @@ import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLEngine;
 import javax.net.ssl.SSLEngineResult;
 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
-import javax.net.ssl.SSLEngineResult.Status;
 import javax.net.ssl.SSLException;
 import javax.net.ssl.SSLSession;
 
 import org.apache.mina.api.IoClient;
 import org.apache.mina.api.IoSession;
-import org.apache.mina.api.IoSession.SessionState;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -83,6 +81,8 @@ public class SslHelper {
 
     /** An empty buffer used during the handshake phase */
     private static final ByteBuffer HANDSHAKE_BUFFER = ByteBuffer.allocate(1024);
+    
+    private ByteBuffer previous = null;
 
     /**
      * Create a new SSL Handler.
@@ -108,6 +108,10 @@ public class SslHelper {
         return sslEngine;
     }
 
+    boolean isHanshaking() {
+        return sslEngine.getHandshakeStatus() != HandshakeStatus.NOT_HANDSHAKING;
+    }
+    
     /**
      * Initialize the SSL handshake.
      *
@@ -155,68 +159,43 @@ public class SslHelper {
     }
 
     /**
-     * Process the NEED_TASK action.
+     * Duplicate a byte buffer for storing it into this context for future
+     * use.
      * 
-     * @param engine The SSLEngine instance
-     * @return The resulting HandshakeStatus
-     * @throws SSLException If we've got an error while processing the tasks
+     * @param buffer the buffer to duplicate
+     * @return the newly allocated buffer
      */
-    private HandshakeStatus processTasks(SSLEngine engine) throws SSLException {
-        Runnable runnable;
-
-        while ((runnable = engine.getDelegatedTask()) != null) {
-            // TODO : we may have to use a thread pool here to improve the
-            // performances
-            runnable.run();
-        }
-
-        HandshakeStatus hsStatus = engine.getHandshakeStatus();
-
-        return hsStatus;
+    private ByteBuffer duplicate(ByteBuffer buffer) {
+        ByteBuffer newBuffer = ByteBuffer.allocateDirect(buffer.remaining() * 2);
+        newBuffer.put(buffer);
+        newBuffer.flip();
+        return newBuffer;
     }
-
+    
     /**
-     * Process the NEED_UNWRAP action. We have to read the incoming buffer, and to feed
-     * the application buffer.
+     * Accumulate the given buffer into the current context. Allocation is performed only
+     * if needed.
+     * 
+     * @param buffer the buffer to accumulate
+     * @return the accumulated buffer
      */
-    private SSLEngineResult unwrap(ByteBuffer inBuffer, ByteBuffer appBuffer) throws SSLException {
-        // First work with either the new incoming buffer, or the accumulating buffer
-        ByteBuffer tempBuffer = inBuffer;
-
-        // Loop until we have processed the entire incoming buffer,
-        // or until we have to stop
-        while (true) {
-            // Do the unwrapping
-            SSLEngineResult result = sslEngine.unwrap(tempBuffer, appBuffer);
-
-            switch (result.getStatus()) {
-            case OK:
-                // Ok, we have unwrapped a message, return.
-                return result;
-
-            case BUFFER_UNDERFLOW:
-                // We need to read some more data from the channel.
-
-                inBuffer.clear();
-
-                return result;
-
-            case CLOSED:
-                // We have received a Close message, we can exit now
-                if (session.isConnectedSecured()) {
-                    return result;
-                } else {
-                    throw new IllegalStateException();
-                }
-
-            case BUFFER_OVERFLOW:
-                // We have to increase the appBuffer size. In any case
-                // we aren't processing an handshake here. Read again.
-                appBuffer = ByteBuffer.allocate(appBuffer.capacity() + 4096);
-            }
+    private ByteBuffer accumulate(ByteBuffer buffer) {
+        if (previous.capacity() - previous.remaining() > buffer.remaining()) {
+            int oldPosition = previous.position();
+            previous.position(previous.limit());
+            previous.limit(previous.limit() + buffer.remaining());
+            previous.put(buffer);
+            previous.position(oldPosition);
+        } else {
+            ByteBuffer newPrevious = ByteBuffer.allocateDirect((previous.remaining() + buffer.remaining() ) * 2);
+            newPrevious.put(previous);
+            newPrevious.put(buffer);
+            newPrevious.flip();
+            previous = newPrevious;
         }
+        return previous;
     }
-
+    
     /**
      * Process a read ByteBuffer over a secured connection, or during the SSL/TLS
      * Handshake.
@@ -226,159 +205,80 @@ public class SslHelper {
      * @throws SSLException If the unwrapping or handshaking failed
      */
     public void processRead(AbstractIoSession session, ByteBuffer readBuffer) throws SSLException {
-        if (session.isConnectedSecured()) {
-            // Unwrap the incoming data
-            while (readBuffer.hasRemaining()) {
-                processUnwrap(session, readBuffer);
-            }
+        ByteBuffer tempBuffer;
+        
+        if (previous != null) {
+            tempBuffer = accumulate(readBuffer);
         } else {
-            // Process the SSL handshake now
-            processHandShake(session, readBuffer);
-        }
-    }
-
-    /**
-     * Unwrap a SSL/TLS message. The message might not be encrypted (if we are processing
-     * a Handshake message or an Alert message).
-     */
-    private void processUnwrap(AbstractIoSession session, ByteBuffer inBuffer) throws SSLException {
-        // Blind guess : once uncompressed, the resulting buffer will be 3 times bigger
-        ByteBuffer appBuffer = ByteBuffer.allocate(inBuffer.limit() * 3);
-        SSLEngineResult result = unwrap(inBuffer, appBuffer);
-
-        switch (result.getStatus()) {
-        case OK:
-            // Ok, go through the chain now
-            appBuffer.flip();
-            session.processMessageReceived(appBuffer);
-            break;
-
-        case CLOSED:
-            // This was a Alert Closure message. Process it
-            processClosed(result);
-
-            break;
-        }
-    }
-
-    /**
-     * Process the SSL/TLS Alert Closure message
-     */
-    private void processClosed(SSLEngineResult result) throws SSLException {
-        // We have received a Alert_CLosure message, we will have to do a wrap
-        HandshakeStatus hsStatus = result.getHandshakeStatus();
-
-        if (hsStatus == HandshakeStatus.NEED_WRAP) {
-            // We need to send back the Alert Closure message
-            if (LOGGER.isDebugEnabled()) {
-                LOGGER.debug("{} processing the NEED_WRAP state", session);
-            }
-
-            int capacity = sslEngine.getSession().getPacketBufferSize();
-            ByteBuffer outBuffer = ByteBuffer.allocate(capacity);
-            session.changeState(SessionState.CONNECTED);
-
-            // Loop until the SSLEngine has nothing more to produce
-            while (!sslEngine.isOutboundDone()) {
-                sslEngine.wrap(EMPTY_BUFFER, outBuffer);
-                outBuffer.flip();
-
-                // Get out of the Connected state
-                WriteRequest writeRequest = new DefaultWriteRequest(outBuffer);
-                session.enqueueWriteRequest(writeRequest);
-            }
+            tempBuffer = readBuffer;
         }
-        session.close(false);
-    }
-
-    /**
-     * Process the SLL/TLS Handshake. We may enter in this method more than once,
-     * as the handshake is a dialogue between the client and the server.
-     */
-    private void processHandShake(IoSession session, ByteBuffer inBuffer) throws SSLException {
-        // Start the Handshake if we aren't already processing a HandShake
-        // and switch to the SECURING state
-        HandshakeStatus hsStatus = sslEngine.getHandshakeStatus();
-
-        // Initilize the session status when we enter into the Handshake process.
-        // Not that we don't call the SSLEngine.beginHandshake() method  :
-        // It's implicitely done internally by the unwrap() method.
-        if (hsStatus == HandshakeStatus.NOT_HANDSHAKING) {
-            session.changeState(SessionState.SECURING);
-        }
-
-        SSLEngineResult result = null;
-
-        // If the SSLEngine has not be started, then the status will be NOT_HANDSHAKING
-        // We loop until we reach the FINISHED state
-        while (hsStatus != HandshakeStatus.FINISHED) {
-            if (hsStatus == HandshakeStatus.NEED_TASK) {
-                hsStatus = processTasks(sslEngine);
-            } else if (hsStatus == HandshakeStatus.NEED_WRAP) {
-                if (LOGGER.isDebugEnabled()) {
-                    LOGGER.debug("{} processing the NEED_WRAP state", session);
+        
+        
+        boolean done = false;
+        SSLEngineResult result;
+        ByteBuffer appBuffer = ByteBuffer.allocateDirect(sslEngine.getSession().getApplicationBufferSize());
+        
+        HandshakeStatus handshakeStatus = sslEngine.getHandshakeStatus();
+        while (!done) {
+            switch (handshakeStatus) {
+            case NEED_UNWRAP:
+            case NOT_HANDSHAKING:
+            case FINISHED:
+              result = sslEngine.unwrap(tempBuffer, appBuffer);
+              handshakeStatus = result.getHandshakeStatus();
+
+              switch (result.getStatus()) {
+              case BUFFER_UNDERFLOW:
+                  /* we need more data */
+                  done = true;
+                  break;
+              case BUFFER_OVERFLOW:
+                  /* resize output buffer */
+                  appBuffer = ByteBuffer.allocateDirect(appBuffer.capacity() * 2);
+                  break;
+              case OK:
+                  if ((handshakeStatus == HandshakeStatus.NOT_HANDSHAKING) &&
+                      (result.bytesProduced() > 0)) {
+                      appBuffer.flip();
+                      session.processMessageReceived(appBuffer);
+                  }
+              }
+              break;
+            case NEED_TASK:
+                Runnable task;
+                
+                while ((task = sslEngine.getDelegatedTask()) != null) {
+                    task.run();
                 }
-
-                // Create an insanely wide buffer, as the SSLEngine requires it
-                int capacity = sslEngine.getSession().getPacketBufferSize();
-                ByteBuffer outBuffer = ByteBuffer.allocate(capacity);
-
-                boolean completed = false;
-
-                // Loop until we are able to wrap the message (we may have
-                // to increase the buffer size more than once.
-                while (!completed) {
-                    result = sslEngine.wrap(EMPTY_BUFFER, outBuffer);
-
-                    switch (result.getStatus()) {
-                    case OK:
-                    case CLOSED:
-                        completed = true;
-                        break;
-
-                    case BUFFER_OVERFLOW:
-                        // Increase the target buffer size
-                        outBuffer = ByteBuffer.allocate(outBuffer.capacity() + 4096);
-                        break;
-                    }
-                }
-
-                // Done. We can now push this buffer into the write queue.
-                outBuffer.flip();
-                WriteRequest writeRequest = new DefaultWriteRequest(inBuffer);
-                writeRequest.setMessage(outBuffer);
-                session.enqueueWriteRequest(writeRequest);
-                hsStatus = result.getHandshakeStatus();
-
-                // Nothing more to wrap : get out.
-                // Note to self : we can probably use only one ByteBuffer for the
-                // multiple wrapped messages. (see https://issues.apache.org/jira/browse/DIRMINA-878)
-                if (hsStatus != HandshakeStatus.NEED_WRAP) {
+                handshakeStatus = sslEngine.getHandshakeStatus();
+                break;
+            case NEED_WRAP:
+                result = sslEngine.wrap(EMPTY_BUFFER, appBuffer);
+                handshakeStatus = result.getHandshakeStatus();
+                switch (result.getStatus()) {
+                case  BUFFER_OVERFLOW:
+                    appBuffer = ByteBuffer.allocateDirect(appBuffer.capacity() * 2);
                     break;
-                }
-            } else if ((hsStatus == HandshakeStatus.NEED_UNWRAP) || (hsStatus == HandshakeStatus.NOT_HANDSHAKING)) {
-                // We cover the ongoing handshake (NEED_UNWRAP) and
-                // the initial call to the handshake (NOT_HANDSHAKING)
-                result = unwrap(inBuffer, HANDSHAKE_BUFFER);
-
-                if (result.getStatus() == Status.BUFFER_UNDERFLOW) {
-                    // Read more data
+                case BUFFER_UNDERFLOW:
+                    done = true;
+                    break;
+                case CLOSED:
+                case OK:
+                    appBuffer.flip();
+                    WriteRequest writeRequest = new DefaultWriteRequest(readBuffer);
+                    writeRequest.setMessage(appBuffer);
+                    session.enqueueWriteRequest(writeRequest);
                     break;
-                } else {
-                    hsStatus = result.getHandshakeStatus();
                 }
             }
         }
-
-        if (hsStatus == HandshakeStatus.FINISHED) {
-            // The handshake has been completed. We can change the session's state.
-            if (LOGGER.isDebugEnabled()) {
-                LOGGER.debug("{} processing the FINISHED state", session);
-            }
-
-            session.changeState(SessionState.SECURED);
+        if (tempBuffer.remaining() > 0) {
+            previous = duplicate(tempBuffer);
+        } else {
+            previous = null;
         }
-    }
+        readBuffer.clear();
+     }
 
     /**
      * Process the application data encryption for a session.

http://git-wip-us.apache.org/repos/asf/mina/blob/2859673d/core/src/test/java/org/apache/mina/transport/tcp/SslTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/mina/transport/tcp/SslTest.java b/core/src/test/java/org/apache/mina/transport/tcp/SslTest.java
index 8a7aa61..3d33ef0 100644
--- a/core/src/test/java/org/apache/mina/transport/tcp/SslTest.java
+++ b/core/src/test/java/org/apache/mina/transport/tcp/SslTest.java
@@ -155,7 +155,7 @@ public class SslTest {
     }
 
     @Test
-    @Ignore("SslHelper needs more attention for big messages")
+    @Ignore("check for fragmentation")
     public void testSSL() throws Exception {
         final int port = startServer();
 
@@ -175,7 +175,6 @@ public class SslTest {
     }
 
     @Test
-    @Ignore("SslHelper needs more attention for big messages")
     public void testBigMessage() throws IOException, GeneralSecurityException, InterruptedException {
         final CountDownLatch counter = new CountDownLatch(1);
         NioTcpServer server = new NioTcpServer();
@@ -195,7 +194,7 @@ public class SslTest {
             @Override
             public void messageReceived(IoSession session, Object message) {
                 receivedSize += ((ByteBuffer) message).remaining();
-                if (receivedSize == 0) {
+                if (receivedSize == messageSize) {
                     counter.countDown();
                 }
             }