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 2014/12/12 21:57:05 UTC
mina git commit: SSL enhancements:
Repository: mina
Updated Branches:
refs/heads/trunk db8d5ff3f -> 9fe463d1d
SSL enhancements:
- new SSL related events: start handshake, complete handshake, secure closed-
- enhance SSL state machine
- add unit tests
Project: http://git-wip-us.apache.org/repos/asf/mina/repo
Commit: http://git-wip-us.apache.org/repos/asf/mina/commit/9fe463d1
Tree: http://git-wip-us.apache.org/repos/asf/mina/tree/9fe463d1
Diff: http://git-wip-us.apache.org/repos/asf/mina/diff/9fe463d1
Branch: refs/heads/trunk
Commit: 9fe463d1d3ec6e4d5e84340e6ac9fbda3b1d22ea
Parents: db8d5ff
Author: Jeff MAURY <je...@apache.org>
Authored: Fri Dec 12 21:56:54 2014 +0100
Committer: Jeff MAURY <je...@apache.org>
Committed: Fri Dec 12 21:56:54 2014 +0100
----------------------------------------------------------------------
.../org/apache/mina/api/AbstractIoFilter.java | 22 ++
.../org/apache/mina/api/AbstractIoHandler.java | 21 ++
.../main/java/org/apache/mina/api/IoFilter.java | 22 ++
.../java/org/apache/mina/api/IoHandler.java | 21 ++
.../mina/service/executor/EventVisitor.java | 6 +
.../mina/service/executor/HandlerCaller.java | 30 ++
.../executor/HandshakeCompletedEvent.java | 48 +++
.../service/executor/HandshakeStartedEvent.java | 48 +++
.../service/executor/SecureClosedEvent.java | 48 +++
.../apache/mina/session/AbstractIoSession.java | 107 +++++-
.../mina/session/DefaultWriteRequest.java | 16 +
.../org/apache/mina/session/WriteRequest.java | 15 +
.../mina/transport/nio/AbstractNioSession.java | 38 ++-
.../apache/mina/transport/nio/SslHelper.java | 209 ++++++++++--
.../org/apache/mina/transport/nio/SslTest.java | 331 +++++++++++++++++--
15 files changed, 905 insertions(+), 77 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mina/blob/9fe463d1/core/src/main/java/org/apache/mina/api/AbstractIoFilter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/mina/api/AbstractIoFilter.java b/core/src/main/java/org/apache/mina/api/AbstractIoFilter.java
index b0f36ee..70f4bc2 100644
--- a/core/src/main/java/org/apache/mina/api/AbstractIoFilter.java
+++ b/core/src/main/java/org/apache/mina/api/AbstractIoFilter.java
@@ -73,4 +73,26 @@ public abstract class AbstractIoFilter implements IoFilter {
@Override
public void messageSent(final IoSession session, final Object message) {
}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void handshakeStarted(IoSession session) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void handshakeCompleted(IoSession session) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void secureClosed(IoSession session) {
+ }
+
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina/blob/9fe463d1/core/src/main/java/org/apache/mina/api/AbstractIoHandler.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/mina/api/AbstractIoHandler.java b/core/src/main/java/org/apache/mina/api/AbstractIoHandler.java
index 05c61af..5d9421a 100644
--- a/core/src/main/java/org/apache/mina/api/AbstractIoHandler.java
+++ b/core/src/main/java/org/apache/mina/api/AbstractIoHandler.java
@@ -87,4 +87,25 @@ public abstract class AbstractIoHandler implements IoHandler {
LOG.error("Unexpected exception, we close the session : ", cause);
session.close(true);
}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void handshakeStarted(IoSession abstractIoSession) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void handshakeCompleted(IoSession session) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void secureClosed(IoSession session) {
+ }
}
http://git-wip-us.apache.org/repos/asf/mina/blob/9fe463d1/core/src/main/java/org/apache/mina/api/IoFilter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/mina/api/IoFilter.java b/core/src/main/java/org/apache/mina/api/IoFilter.java
index 7e53ab6..9dcb8fb 100644
--- a/core/src/main/java/org/apache/mina/api/IoFilter.java
+++ b/core/src/main/java/org/apache/mina/api/IoFilter.java
@@ -75,4 +75,26 @@ public interface IoFilter {
* @param message the incoming message to process
*/
void messageSent(IoSession session, Object message);
+
+ /**
+ * Invoked when a secure handshake has been started.
+ *
+ * @param session {@link IoSession} associated with the invocation
+ */
+ void handshakeStarted(IoSession session);
+
+ /**
+ * Invoked when a secure handshake has been completed.
+ *
+ * @param session {@link IoSession} associated with the invocation
+ */
+ void handshakeCompleted(IoSession session);
+
+ /**
+ * Invoked when a secure context has been closed.
+ *
+ * @param session {@link IoSession} associated with the invocation
+ */
+ void secureClosed(IoSession session);
+
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina/blob/9fe463d1/core/src/main/java/org/apache/mina/api/IoHandler.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/mina/api/IoHandler.java b/core/src/main/java/org/apache/mina/api/IoHandler.java
index b3a6acf..1c214fd 100644
--- a/core/src/main/java/org/apache/mina/api/IoHandler.java
+++ b/core/src/main/java/org/apache/mina/api/IoHandler.java
@@ -92,4 +92,25 @@ public interface IoHandler {
* @param cause the caught exception
*/
void exceptionCaught(IoSession session, Exception cause);
+
+ /**
+ * Invoked for secured session when the handshake has been started. May be called
+ * several times for a single session in case of rehandshake.
+ * @param session {@link IoSession} associated with the invocation
+ */
+ void handshakeStarted(IoSession abstractIoSession);
+
+ /**
+ * Invoked for secured session when the handshake has been completed. May be called
+ * several times for a single session in case of rehandshake.
+ * @param session {@link IoSession} associated with the invocation
+ */
+ void handshakeCompleted(IoSession session);
+
+ /**
+ * Invoked for secured session when underlying SSL/TLS session has been closed.
+ * @param session {@link IoSession} associated with the invocation
+ */
+ void secureClosed(IoSession session);
+
}
http://git-wip-us.apache.org/repos/asf/mina/blob/9fe463d1/core/src/main/java/org/apache/mina/service/executor/EventVisitor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/mina/service/executor/EventVisitor.java b/core/src/main/java/org/apache/mina/service/executor/EventVisitor.java
index 1e8ecc3..6ece82c 100644
--- a/core/src/main/java/org/apache/mina/service/executor/EventVisitor.java
+++ b/core/src/main/java/org/apache/mina/service/executor/EventVisitor.java
@@ -35,4 +35,10 @@ public interface EventVisitor {
void visit(SentEvent event);
void visit(IdleEvent event);
+
+ void visit(HandshakeStartedEvent event);
+
+ void visit(HandshakeCompletedEvent handshakeCompletedEvent);
+
+ void visit(SecureClosedEvent secureClosedEvent);
}
http://git-wip-us.apache.org/repos/asf/mina/blob/9fe463d1/core/src/main/java/org/apache/mina/service/executor/HandlerCaller.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/mina/service/executor/HandlerCaller.java b/core/src/main/java/org/apache/mina/service/executor/HandlerCaller.java
index 47fdb42..f0f8095 100644
--- a/core/src/main/java/org/apache/mina/service/executor/HandlerCaller.java
+++ b/core/src/main/java/org/apache/mina/service/executor/HandlerCaller.java
@@ -81,4 +81,34 @@ class HandlerCaller implements EventVisitor {
session.getService().getIoHandler().exceptionCaught(session, e);
}
}
+
+ @Override
+ public void visit(HandshakeStartedEvent event) {
+ IoSession session = event.getSession();
+ try {
+ session.getService().getIoHandler().handshakeStarted(session);
+ } catch (Exception e) {
+ session.getService().getIoHandler().exceptionCaught(session, e);
+ }
+ }
+
+ @Override
+ public void visit(HandshakeCompletedEvent event) {
+ IoSession session = event.getSession();
+ try {
+ session.getService().getIoHandler().handshakeCompleted(session);
+ } catch (Exception e) {
+ session.getService().getIoHandler().exceptionCaught(session, e);
+ }
+ }
+
+ @Override
+ public void visit(SecureClosedEvent event) {
+ IoSession session = event.getSession();
+ try {
+ session.getService().getIoHandler().secureClosed(session);
+ } catch (Exception e) {
+ session.getService().getIoHandler().exceptionCaught(session, e);
+ }
+ }
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina/blob/9fe463d1/core/src/main/java/org/apache/mina/service/executor/HandshakeCompletedEvent.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/mina/service/executor/HandshakeCompletedEvent.java b/core/src/main/java/org/apache/mina/service/executor/HandshakeCompletedEvent.java
new file mode 100644
index 0000000..b9bdbf9
--- /dev/null
+++ b/core/src/main/java/org/apache/mina/service/executor/HandshakeCompletedEvent.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Founimport org.apache.mina.api.IoSession;
+tributor 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.service.executor;
+
+import org.apache.mina.api.IoSession;
+
+/**
+ * An {@link IoSession} handshake started {@link Event}
+ *
+ * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+ */
+public class HandshakeCompletedEvent implements Event {
+ private final IoSession session;
+
+ public HandshakeCompletedEvent(final IoSession session) {
+ this.session = session;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public IoSession getSession() {
+ return session;
+ }
+
+ @Override
+ public void visit(EventVisitor visitor) {
+ visitor.visit(this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina/blob/9fe463d1/core/src/main/java/org/apache/mina/service/executor/HandshakeStartedEvent.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/mina/service/executor/HandshakeStartedEvent.java b/core/src/main/java/org/apache/mina/service/executor/HandshakeStartedEvent.java
new file mode 100644
index 0000000..f5ca879
--- /dev/null
+++ b/core/src/main/java/org/apache/mina/service/executor/HandshakeStartedEvent.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Founimport org.apache.mina.api.IoSession;
+tributor 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.service.executor;
+
+import org.apache.mina.api.IoSession;
+
+/**
+ * An {@link IoSession} handshake started {@link Event}
+ *
+ * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+ */
+public class HandshakeStartedEvent implements Event {
+ private final IoSession session;
+
+ public HandshakeStartedEvent(final IoSession session) {
+ this.session = session;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public IoSession getSession() {
+ return session;
+ }
+
+ @Override
+ public void visit(EventVisitor visitor) {
+ visitor.visit(this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina/blob/9fe463d1/core/src/main/java/org/apache/mina/service/executor/SecureClosedEvent.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/mina/service/executor/SecureClosedEvent.java b/core/src/main/java/org/apache/mina/service/executor/SecureClosedEvent.java
new file mode 100644
index 0000000..166c6c6
--- /dev/null
+++ b/core/src/main/java/org/apache/mina/service/executor/SecureClosedEvent.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Founimport org.apache.mina.api.IoSession;
+tributor 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.service.executor;
+
+import org.apache.mina.api.IoSession;
+
+/**
+ * An {@link IoSession} secure closed {@link Event}
+ *
+ * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+ */
+public class SecureClosedEvent implements Event {
+ private final IoSession session;
+
+ public SecureClosedEvent(final IoSession session) {
+ this.session = session;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public IoSession getSession() {
+ return session;
+ }
+
+ @Override
+ public void visit(EventVisitor visitor) {
+ visitor.visit(this);
+ }
+}
http://git-wip-us.apache.org/repos/asf/mina/blob/9fe463d1/core/src/main/java/org/apache/mina/session/AbstractIoSession.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/mina/session/AbstractIoSession.java b/core/src/main/java/org/apache/mina/session/AbstractIoSession.java
index 1504c13..5ec8cc2 100644
--- a/core/src/main/java/org/apache/mina/session/AbstractIoSession.java
+++ b/core/src/main/java/org/apache/mina/session/AbstractIoSession.java
@@ -40,10 +40,13 @@ import org.apache.mina.api.IoSessionConfig;
import org.apache.mina.filterchain.ReadFilterChainController;
import org.apache.mina.filterchain.WriteFilterChainController;
import org.apache.mina.service.executor.CloseEvent;
+import org.apache.mina.service.executor.HandshakeCompletedEvent;
+import org.apache.mina.service.executor.HandshakeStartedEvent;
import org.apache.mina.service.executor.IdleEvent;
import org.apache.mina.service.executor.IoHandlerExecutor;
import org.apache.mina.service.executor.OpenEvent;
import org.apache.mina.service.executor.ReceiveEvent;
+import org.apache.mina.service.executor.SecureClosedEvent;
import org.apache.mina.service.executor.SentEvent;
import org.apache.mina.service.idlechecker.IdleChecker;
import org.apache.mina.transport.nio.SelectorLoop;
@@ -330,14 +333,8 @@ public abstract class AbstractIoSession implements IoSession, ReadFilterChainCon
*/
@Override
public boolean isSecured() {
- return secured;
- }
-
- /**
- * {@inheritDoc}
- */
- public void setSecured(boolean secured) {
- this.secured = secured;
+ SslHelper helper = attributes.getAttribute(SSL_HELPER);
+ return helper != null && helper.isActive();
}
/**
@@ -347,9 +344,7 @@ public abstract class AbstractIoSession implements IoSession, ReadFilterChainCon
public void initSecure(SSLContext sslContext) {
SslHelper sslHelper = new SslHelper(this, sslContext);
sslHelper.init();
-
attributes.setAttribute(SSL_HELPER, sslHelper);
- setSecured(true);
}
/**
@@ -765,6 +760,98 @@ public abstract class AbstractIoSession implements IoSession, ReadFilterChainCon
}
+ public void processHandshakeStarted() {
+ if (IS_DEBUG) {
+ LOG.debug("processing handshake started event for session {}", this);
+ }
+
+ try {
+ int size = chain.length;
+
+ for (int i = size - 1; i >= 0; i--) {
+ chain[i].handshakeStarted(this);
+ }
+
+ IoHandler handler = getService().getIoHandler();
+
+ if (handler != null) {
+ IoHandlerExecutor executor = getService().getIoHandlerExecutor();
+
+ if (executor != null) {
+ // asynchronous event
+ executor.execute(new HandshakeStartedEvent(this));
+ } else {
+ // synchronous call (in the I/O loop)
+ handler.handshakeStarted(this);
+ }
+ }
+ } catch (RuntimeException e) {
+ processException(e);
+ }
+
+ }
+
+ public void processHandshakeCompleted() {
+ if (IS_DEBUG) {
+ LOG.debug("processing handshake completed event for session {}", this);
+ }
+
+ try {
+ int size = chain.length;
+
+ for (int i = size - 1; i >= 0; i--) {
+ chain[i].handshakeCompleted(this);
+ }
+
+ IoHandler handler = getService().getIoHandler();
+
+ if (handler != null) {
+ IoHandlerExecutor executor = getService().getIoHandlerExecutor();
+
+ if (executor != null) {
+ // asynchronous event
+ executor.execute(new HandshakeCompletedEvent(this));
+ } else {
+ // synchronous call (in the I/O loop)
+ handler.handshakeCompleted(this);
+ }
+ }
+ } catch (RuntimeException e) {
+ processException(e);
+ }
+
+ }
+
+ public void processSecureClosed() {
+ if (IS_DEBUG) {
+ LOG.debug("processing secure closed event for session {}", this);
+ }
+
+ try {
+ int size = chain.length;
+
+ for (int i = size - 1; i >= 0; i--) {
+ chain[i].secureClosed(this);
+ }
+
+ IoHandler handler = getService().getIoHandler();
+
+ if (handler != null) {
+ IoHandlerExecutor executor = getService().getIoHandlerExecutor();
+
+ if (executor != null) {
+ // asynchronous event
+ executor.execute(new SecureClosedEvent(this));
+ } else {
+ // synchronous call (in the I/O loop)
+ handler.secureClosed(this);
+ }
+ }
+ } catch (RuntimeException e) {
+ processException(e);
+ }
+ }
+
/**
* process session message received event using the filter chain. To be called by the session {@link SelectorLoop} .
*
http://git-wip-us.apache.org/repos/asf/mina/blob/9fe463d1/core/src/main/java/org/apache/mina/session/DefaultWriteRequest.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/mina/session/DefaultWriteRequest.java b/core/src/main/java/org/apache/mina/session/DefaultWriteRequest.java
index 3269efd..2e0b5a8 100644
--- a/core/src/main/java/org/apache/mina/session/DefaultWriteRequest.java
+++ b/core/src/main/java/org/apache/mina/session/DefaultWriteRequest.java
@@ -38,6 +38,12 @@ public class DefaultWriteRequest implements WriteRequest {
/** the future to complete when this message is written */
private IoFuture<Void> future;
+
+ /**
+ * The secure internal flag that tells if the message must be encrypted
+ * when sent (false) or not (true)
+ */
+ private boolean secureInternal = false;
/**
* Creates a new instance of a WriteRequest, storing the message as it was
@@ -142,4 +148,14 @@ public class DefaultWriteRequest implements WriteRequest {
return sb.toString();
}
+
+ @Override
+ public boolean isSecureInternal() {
+ return secureInternal;
+ }
+
+ @Override
+ public void setSecureInternal(boolean secureInternal) {
+ this.secureInternal = secureInternal;
+ }
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina/blob/9fe463d1/core/src/main/java/org/apache/mina/session/WriteRequest.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/mina/session/WriteRequest.java b/core/src/main/java/org/apache/mina/session/WriteRequest.java
index b525892..cb43354 100644
--- a/core/src/main/java/org/apache/mina/session/WriteRequest.java
+++ b/core/src/main/java/org/apache/mina/session/WriteRequest.java
@@ -69,4 +69,19 @@ public interface WriteRequest {
* @param the future
*/
void setFuture(IoFuture<Void> future);
+
+ /**
+ * Get the flag that tells that the underlying message is an internal one,
+ * not needed to be encrypted
+ * @return the internal secure flag of the message
+ */
+ boolean isSecureInternal();
+
+
+ /**
+ * Set the flag that tells that the underlying message is an internal one,
+ * not needed to be encrypted
+ * @param secureInternal the secure internal flag
+ */
+ void setSecureInternal(boolean secureInternal);
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/mina/blob/9fe463d1/core/src/main/java/org/apache/mina/transport/nio/AbstractNioSession.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/mina/transport/nio/AbstractNioSession.java b/core/src/main/java/org/apache/mina/transport/nio/AbstractNioSession.java
index c66fd1f..41a97b0 100644
--- a/core/src/main/java/org/apache/mina/transport/nio/AbstractNioSession.java
+++ b/core/src/main/java/org/apache/mina/transport/nio/AbstractNioSession.java
@@ -26,6 +26,9 @@ import java.nio.channels.SocketChannel;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicBoolean;
+import javax.net.ssl.SSLException;
+
+import org.apache.mina.api.IoClient;
import org.apache.mina.api.IoFuture;
import org.apache.mina.api.IoService;
import org.apache.mina.api.IoSession;
@@ -40,7 +43,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * Common ancestor for NIO based {@link IoSession} implmentation.
+ * Common ancestor for NIO based {@link IoSession} implementation.
*
* @author <a href="http://mina.apache.org">Apache MINA Project</a>
*/
@@ -99,6 +102,18 @@ public abstract class AbstractNioSession extends AbstractIoSession {
}
};
+ @Override
+ public void processSessionOpen() {
+ super.processSessionOpen();
+ try {
+ if (isSecured() && getService() instanceof IoClient) {
+ getAttribute(SSL_HELPER).beginHandshake();
+ }
+ } catch (IOException e) {
+ processException(e);
+ }
+ }
+
/**
* {@inheritDoc}
*/
@@ -114,6 +129,16 @@ public abstract class AbstractNioSession extends AbstractIoSession {
channelClose();
processSessionClosed();
} else {
+ if (isSecured()) {
+ SslHelper sslHelper = getAttribute(SSL_HELPER, null);
+ if (sslHelper != null) {
+ try {
+ sslHelper.close();
+ } catch (IOException e) {
+ processException(e);
+ }
+ }
+ }
// flush this session the flushing code will close the session
flushWriteQueue();
}
@@ -146,7 +171,7 @@ public abstract class AbstractNioSession extends AbstractIoSession {
LOG.debug("enqueueWriteRequest {}", writeRequest);
}
- if (isConnectedSecured()) {
+ if (isSecured()) {
// SSL/TLS : we have to encrypt the message
SslHelper sslHelper = getAttribute(SSL_HELPER, null);
@@ -154,10 +179,12 @@ public abstract class AbstractNioSession extends AbstractIoSession {
throw new IllegalStateException();
}
- writeRequest = sslHelper.processWrite(this, writeRequest.getMessage(), writeQueue);
+ if (!writeRequest.isSecureInternal()) {
+ writeRequest = sslHelper.processWrite(this, writeRequest.getMessage(), writeQueue);
+ }
}
- /*synchronized (writeQueue)*/{
+ if (writeRequest != null) {
ByteBuffer message = (ByteBuffer) writeRequest.getMessage();
if (writeQueue.isEmpty()) {
@@ -214,6 +241,9 @@ public abstract class AbstractNioSession extends AbstractIoSession {
// We have to push the request on the writeQueue
writeQueue.add(writeRequest);
+ if (!registeredForWrite.getAndSet(true)) {
+ flushWriteQueue();
+ }
}
}
http://git-wip-us.apache.org/repos/asf/mina/blob/9fe463d1/core/src/main/java/org/apache/mina/transport/nio/SslHelper.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/mina/transport/nio/SslHelper.java b/core/src/main/java/org/apache/mina/transport/nio/SslHelper.java
index 889a65f..83cce1b 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
@@ -21,14 +21,17 @@ package org.apache.mina.transport.nio;
import static org.apache.mina.session.AttributeKey.createKey;
+import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
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;
@@ -58,7 +61,31 @@ public class SslHelper {
/** The current session */
private final IoSession session;
+
+ /**
+ * The internal secure state of the session.
+ * CREDENTIALS_NOT_YET_AVAILABLE: the session is currently handskaking, application messages
+ * will be queued before being encrypted and sent.
+ * CREDENTAILS_AVAILABLE: the session has completed handshake, application messages
+ * can be encrypted and sent as they are submitted.
+ * NO_CREDENTIALS: secure credentials are removed from the session, application messages
+ * are not encrypted anymore.
+ *
+ */
+ enum State {
+ CREDENTIALS_NOT_YET_AVAILABLE,
+ CREDENTAILS_AVAILABLE,
+ NO_CREDENTIALS
+ }
+ private State state = State.CREDENTIALS_NOT_YET_AVAILABLE;
+
+ /**
+ * The list of applications messages queued because submitted while the initial handshake was
+ * not yet finished.
+ */
+ private ConcurrentLinkedQueue<WriteRequest> messages = new ConcurrentLinkedQueue<WriteRequest>();
+
/**
* 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
@@ -105,9 +132,22 @@ public class SslHelper {
return sslEngine;
}
+ /**
+ * Return the state (credentials state) of the session.
+ *
+ * @return the credentials state
+ */
+ State getState() {
+ return state;
+ }
+
boolean isHanshaking() {
return sslEngine.getHandshakeStatus() != HandshakeStatus.NOT_HANDSHAKING;
}
+
+ public boolean isActive() {
+ return state != State.NO_CREDENTIALS;
+ }
/**
* Initialize the SSL handshake.
@@ -119,7 +159,7 @@ public class SslHelper {
return;
}
- LOGGER.debug("{} Initializing the SSL Helper", session);
+ LOGGER.debug("{} Initializing the SSLEngine", session);
InetSocketAddress peer = session.getAttribute(PEER_ADDRESS, null);
@@ -217,7 +257,7 @@ public class SslHelper {
case NOT_HANDSHAKING:
case FINISHED:
result = sslEngine.unwrap(tempBuffer, appBuffer);
- handshakeStatus = result.getHandshakeStatus();
+ processResult(session, handshakeStatus, result);
switch (result.getStatus()) {
case BUFFER_UNDERFLOW:
@@ -233,6 +273,14 @@ public class SslHelper {
appBuffer.flip();
session.processMessageReceived(appBuffer);
}
+ break;
+ case CLOSED:
+ break;
+ }
+ if (sslEngine != null) {
+ handshakeStatus = sslEngine.getHandshakeStatus();
+ } else {
+ done = true;
}
break;
case NEED_TASK:
@@ -245,7 +293,7 @@ public class SslHelper {
break;
case NEED_WRAP:
result = sslEngine.wrap(EMPTY_BUFFER, appBuffer);
- handshakeStatus = result.getHandshakeStatus();
+ processResult(session, handshakeStatus, result);
switch (result.getStatus()) {
case BUFFER_OVERFLOW:
appBuffer = ByteBuffer.allocateDirect(appBuffer.capacity() * 2);
@@ -256,11 +304,20 @@ public class SslHelper {
case CLOSED:
case OK:
appBuffer.flip();
- WriteRequest writeRequest = new DefaultWriteRequest(readBuffer);
+ WriteRequest writeRequest = new DefaultWriteRequest(appBuffer);
writeRequest.setMessage(appBuffer);
+ writeRequest.setSecureInternal(true);
session.enqueueWriteRequest(writeRequest);
break;
}
+ if (sslEngine != null) {
+ handshakeStatus = sslEngine.getHandshakeStatus();
+ } else {
+ done = true;
+ }
+ }
+ if (handshakeStatus == HandshakeStatus.FINISHED) {
+ state = State.CREDENTAILS_AVAILABLE;
}
}
if (tempBuffer.remaining() > 0) {
@@ -272,7 +329,61 @@ public class SslHelper {
}
/**
- * Process the application data encryption for a session.
+ * Process the close event from the SSL engine. If the closed event has not been
+ * processed, then send an event.
+ *
+ * @param session the {@link AbstractIoSession} MINA internal IO session
+ */
+ void switchToNoSecure(AbstractIoSession session) {
+ if (state != State.NO_CREDENTIALS) {
+ session.processSecureClosed();
+ state = State.NO_CREDENTIALS;
+ sslEngine = null;
+ }
+ }
+
+ /**
+ * Process the session handshake status and the last operation result in order to
+ * update the internal state and propagate handshake related events.
+ *
+ * @param session the {@link AbstractIoSession} MINA internal IO session
+ * @param sessionStatus the last session handshake status
+ * @param operationStatus the returned operation status
+ */
+ private void processResult(AbstractIoSession session, HandshakeStatus sessionStatus, SSLEngineResult result) {
+ LOGGER.debug("handshake status:" + sessionStatus + " engine result:" + result);
+ switch (sessionStatus) {
+ case NEED_TASK:
+ case NEED_UNWRAP:
+ case NEED_WRAP:
+ if (result.getHandshakeStatus() == HandshakeStatus.FINISHED) {
+ state = State.CREDENTAILS_AVAILABLE;
+ session.processHandshakeCompleted();
+ for(WriteRequest request : messages) {
+ session.enqueueWriteRequest(request);
+ }
+ messages.clear();
+ }
+ if (result.getStatus() == Status.CLOSED) {
+ switchToNoSecure(session);
+ }
+ break;
+ case FINISHED:
+ case NOT_HANDSHAKING:
+ if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
+ session.processHandshakeStarted();
+ }
+ break;
+ }
+ }
+
+ /**
+ * Process the application data encryption for a session. As the SSLEngine is record
+ * oriented, then depending on the message size, this may lead to several encrypted
+ * messages to be generated. So, if n messages are generated, the first n-1 will
+ * be queued and the last one will be returned. It will be automatically added
+ * to the end of the queue by the called because a non empty queue will be
+ * detected.
*
* @param session The session sending encrypted data to the peer.
* @param message The message to encrypt
@@ -280,35 +391,69 @@ public class SslHelper {
* @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;
+ WriteRequest processWrite(AbstractIoSession session, Object message, Queue<WriteRequest> writeQueue) {
+ WriteRequest request = null;
+
+ switch (state) {
+ case CREDENTAILS_AVAILABLE:
+ ByteBuffer buf = (ByteBuffer) message;
+ ByteBuffer appBuffer = ByteBuffer.allocate(sslEngine.getSession().getPacketBufferSize());
+ try {
+ boolean done = false;
+ while (!done) {
+ // 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 CLOSED:
+ switchToNoSecure(session);
+ done = true;
+ break;
+
+ case BUFFER_UNDERFLOW:
+ case OK:
+ // We are done. Flip the buffer and push it to the write queue.
+ appBuffer.flip();
+ done = buf.remaining() == 0;
+ if (done) {
+ request = new DefaultWriteRequest(appBuffer);
+ } else {
+ writeQueue.offer(new DefaultWriteRequest(appBuffer));
+ appBuffer = ByteBuffer.allocateDirect(appBuffer.capacity());
+ }
+ break;
+ }
}
+ } catch (SSLException se) {
+ throw new IllegalStateException(se.getMessage());
}
- } catch (SSLException se) {
- throw new IllegalStateException(se.getMessage());
+ break;
+ case CREDENTIALS_NOT_YET_AVAILABLE:
+ messages.add(new DefaultWriteRequest(message));
+ break;
+ case NO_CREDENTIALS:
+ request = new DefaultWriteRequest(message);
+ break;
+ }
+ return request;
+ }
+
+ public void beginHandshake() throws IOException {
+ if (sslEngine != null) {
+ ((AbstractIoSession)session).processHandshakeStarted();
+ sslEngine.beginHandshake();
+ processRead((AbstractIoSession) session, EMPTY_BUFFER);
+ }
+ }
+
+ public void close() throws IOException {
+ if (sslEngine != null) {
+ sslEngine.closeOutbound();
+ processRead((AbstractIoSession) session, EMPTY_BUFFER);
}
}
}
http://git-wip-us.apache.org/repos/asf/mina/blob/9fe463d1/core/src/test/java/org/apache/mina/transport/nio/SslTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/mina/transport/nio/SslTest.java b/core/src/test/java/org/apache/mina/transport/nio/SslTest.java
index fad8428..ad4cb1c 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
@@ -19,24 +19,40 @@
*/
package org.apache.mina.transport.nio;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.WriteAbortedException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
+import java.net.ServerSocket;
import java.net.Socket;
+import java.net.UnknownHostException;
import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.WritableByteChannel;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.Security;
+import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.net.SocketFactory;
+import javax.net.ssl.HandshakeCompletedEvent;
+import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocketFactory;
+import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
@@ -45,6 +61,7 @@ import org.apache.mina.api.IoSession;
import org.apache.mina.transport.nio.NioTcpServer;
import org.junit.Ignore;
import org.junit.Test;
+import org.slf4j.LoggerFactory;
/**
* Test a SSL session where the connection is established and closed twice. It should be
@@ -72,36 +89,54 @@ public class SslTest {
}
private static class TestHandler extends AbstractIoHandler {
+ private String data = "";
+ private boolean second = false;
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'");
+ data += line;
+ if (!second && data.startsWith("hello")) {
+ second = true;
+ } else if (second && data.contains("send")) {
session.write(Charset.defaultCharset().encode("data\n"));
+ data = "";
+ second = false;
}
}
}
+
+ private static enum Client {
+ JDK,
+ MINA_BEFORE_HANDSHAKE,
+ MINA_AFTER_HANDSHAKE;
+ }
/**
* Starts a Server with the SSL Filter and a simple text line
* protocol codec filter
*/
- private static int startServer() throws Exception {
+ private static NioTcpServer startServer(AbstractIoHandler handler) throws Exception {
NioTcpServer server = new NioTcpServer();
server.setReuseAddress(true);
server.getSessionConfig().setSslContext(createSSLContext());
- server.setIoHandler(new TestHandler());
+ server.setIoHandler(handler);
server.bind(new InetSocketAddress(0));
- return server.getServerSocketChannel().socket().getLocalPort();
+ return server;
+ }
+
+ private static NioTcpClient startClient(AbstractIoHandler handler, int port) throws Exception {
+ NioTcpClient client = new NioTcpClient();
+
+ client.getSessionConfig().setSslContext(createSSLContext());
+ client.setIoHandler(handler);
+ client.connect(new InetSocketAddress("localhost", port));
+ return client;
}
/**
* Starts a client which will connect twice using SSL
*/
- private static void startClient(int port) throws Exception {
+ private static void startJDKClient(int port) throws Exception {
address = InetAddress.getByName("localhost");
SSLContext context = createSSLContext();
@@ -123,7 +158,7 @@ public class SslTest {
socket.setSoTimeout(10000);
System.out.println("Client sending: send");
- socket.getOutputStream().write("send\n".getBytes());
+ socket.getOutputStream().write("send \n".getBytes());
socket.getOutputStream().flush();
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
@@ -154,14 +189,14 @@ public class SslTest {
}
@Test
- @Ignore("check for fragmentation")
+ //@Ignore("check for fragmentation")
public void testSSL() throws Exception {
- final int port = startServer();
+ final NioTcpServer server = startServer(new TestHandler());
Thread t = new Thread() {
public void run() {
try {
- startClient(port);
+ startJDKClient(server.getServerSocketChannel().socket().getLocalPort());
} catch (Exception e) {
clientError = e;
}
@@ -169,21 +204,147 @@ public class SslTest {
};
t.start();
t.join();
+ server.unbind();
if (clientError != null)
throw clientError;
}
@Test
- public void testBigMessage() throws IOException, GeneralSecurityException, InterruptedException {
- final CountDownLatch counter = new CountDownLatch(1);
- NioTcpServer server = new NioTcpServer();
- final int messageSize = 1 * 1024 * 1024;
+ public void checkThatSecureEventsArePropagatedServerSide() throws Exception {
+ final AtomicInteger startHandshakeCount = new AtomicInteger();
+ final AtomicInteger completedHandshakeCount = new AtomicInteger();
+ final AtomicInteger secureClosedCount = new AtomicInteger();
+ final CountDownLatch closedCount = new CountDownLatch(1);
+ final NioTcpServer server = startServer(new AbstractIoHandler() {
+
+ @Override
+ public void handshakeStarted(IoSession abstractIoSession) {
+ startHandshakeCount.incrementAndGet();
+ }
+
+ @Override
+ public void handshakeCompleted(IoSession session) {
+ completedHandshakeCount.incrementAndGet();
+ }
+
+ @Override
+ public void secureClosed(IoSession session) {
+ secureClosedCount.incrementAndGet();
+ }
+
+ @Override
+ public void sessionClosed(IoSession session) {
+ closedCount.countDown();
+ }
+ });
+ SSLSocketFactory factory = createSSLContext().getSocketFactory();
+ SSLSocket s = (SSLSocket) factory.createSocket("localhost", server.getServerSocketChannel().socket().getLocalPort());
+ s.startHandshake();
+ s.close();
+ assertTrue(closedCount.await(10, TimeUnit.SECONDS));
+ assertEquals(1, startHandshakeCount.get());
+ assertEquals(1, completedHandshakeCount.get());
+ assertEquals(1, secureClosedCount.get());
+ }
+
+ @Test
+ public void checkThatSecureEventsArePropagatedServerSideWithSecondHandshake() throws Exception {
+ final CountDownLatch closeCount = new CountDownLatch(1);
+ final AtomicInteger startHandshakeCount = new AtomicInteger();
+ final AtomicInteger completedHandshakeCount = new AtomicInteger();
+ final AtomicInteger secureClosedCount = new AtomicInteger();
+ final NioTcpServer server = startServer(new AbstractIoHandler() {
+
+ @Override
+ public void handshakeStarted(IoSession abstractIoSession) {
+ startHandshakeCount.incrementAndGet();
+ }
+
+ @Override
+ public void handshakeCompleted(IoSession session) {
+ completedHandshakeCount.incrementAndGet();
+ }
+
+ @Override
+ public void secureClosed(IoSession session) {
+ secureClosedCount.incrementAndGet();
+ }
+
+ @Override
+ public void sessionClosed(IoSession session) {
+ closeCount.countDown();
+ }
+ });
+ SSLSocketFactory factory = createSSLContext().getSocketFactory();
+ SSLSocket s = (SSLSocket) factory.createSocket("localhost", server.getServerSocketChannel().socket().getLocalPort());
+ final AtomicInteger handskaheCounter = new AtomicInteger();
+ s.addHandshakeCompletedListener(new HandshakeCompletedListener() {
+ @Override
+ public void handshakeCompleted(HandshakeCompletedEvent event) {
+ final int count = handskaheCounter.getAndIncrement();
+ if (count == 0) {
+ try {
+ event.getSocket().startHandshake();
+ event.getSocket().setSoTimeout(5000);
+ event.getSocket().getInputStream().read();
+ }
+ catch (IOException e) {}
+ } else {
+ try {
+ event.getSocket().close();
+ }
+ catch (IOException e) {}
+ }
+ }
+ });
+ s.startHandshake();
+ assertTrue(closeCount.await(10, TimeUnit.SECONDS));
+ assertEquals(2, startHandshakeCount.get());
+ assertEquals(2, completedHandshakeCount.get());
+ assertEquals(1, secureClosedCount.get());
+ server.unbind();
+ }
+
+ @Test
+ public void checkThatSecureEventsArePropagatedClientSide() throws Exception {
+ final AtomicInteger startHandshakeCount = new AtomicInteger();
+ final AtomicInteger completedHandshakeCount = new AtomicInteger();
+ final AtomicInteger secureClosedCount = new AtomicInteger();
+ final CountDownLatch closeCount = new CountDownLatch(1);
+ final NioTcpServer server = startServer(new AbstractIoHandler() {});
+ final NioTcpClient client = startClient(new AbstractIoHandler() {
+ @Override
+ public void handshakeStarted(IoSession abstractIoSession) {
+ startHandshakeCount.incrementAndGet();
+ }
+
+ @Override
+ public void handshakeCompleted(IoSession session) {
+ completedHandshakeCount.incrementAndGet();
+ session.close(false);
+ }
+
+ @Override
+ public void secureClosed(IoSession session) {
+ secureClosedCount.incrementAndGet();
+ }
+
+ @Override
+ public void sessionClosed(IoSession session) {
+ closeCount.countDown();
+ }
+ }, server.getServerSocketChannel().socket().getLocalPort());
+ assertTrue(closeCount.await(10, TimeUnit.SECONDS));
+ assertEquals(1, startHandshakeCount.get());
+ assertEquals(1, completedHandshakeCount.get());
+ assertEquals(1, secureClosedCount.get());
+ }
- /*
- * Server
- */
+ private static NioTcpServer createReceivingServer(final int size, final CountDownLatch counter, final OutputStream stream) throws IOException, GeneralSecurityException {
+ NioTcpServer server = new NioTcpServer();
server.setReuseAddress(true);
server.getSessionConfig().setSslContext(createSSLContext());
+ final WritableByteChannel channel = (stream!=null)?Channels.newChannel(stream):null;
server.setIoHandler(new AbstractIoHandler() {
private int receivedSize = 0;
@@ -193,22 +354,130 @@ public class SslTest {
@Override
public void messageReceived(IoSession session, Object message) {
receivedSize += ((ByteBuffer) message).remaining();
- if (receivedSize == messageSize) {
+ if (channel != null) {
+ try {
+ channel.write((ByteBuffer) message);
+ } catch (IOException e) {
+ exceptionCaught(session, e);
+ }
+ }
+ if (receivedSize == size) {
counter.countDown();
+ if (channel != null) {
+ try {
+ channel.close();
+ } catch (IOException e) {
+ exceptionCaught(session, e);
+ }
+ }
}
}
});
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));
+ return server;
+ }
+
+ protected void testMessage(final int size, final Client clientType) throws IOException, GeneralSecurityException, InterruptedException {
+ final CountDownLatch counter = new CountDownLatch(1);
+ final byte[] message = new byte[size];
+ new Random().nextBytes(message);
+ final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ try {
+ /*
+ * Server
+ */
+ NioTcpServer server = createReceivingServer(size, counter, bos);
+ try {
+ int port = server.getServerSocketChannel().socket().getLocalPort();
+ /*
+ * Client
+ */
+ if (clientType == Client.JDK) {
+ Socket socket = server.getSessionConfig().getSslContext().getSocketFactory()
+ .createSocket("localhost", port);
+ socket.getOutputStream().write(message);
+ socket.getOutputStream().flush();
+ socket.close();
+ } else {
+ NioTcpClient client = new NioTcpClient();
+ client.setIoHandler(new AbstractIoHandler() {
+ int sendSize = 0;
+
+ @Override
+ public void sessionOpened(IoSession session) {
+ if (clientType == Client.MINA_BEFORE_HANDSHAKE) {
+ session.write(ByteBuffer.wrap(message));
+ }
+ }
+
+ @Override
+ public void handshakeCompleted(IoSession session) {
+ if (clientType == Client.MINA_AFTER_HANDSHAKE) {
+ session.write(ByteBuffer.wrap(message));
+ }
+ }
+
+ @Override
+ public void messageSent(IoSession session, Object message) {
+ sendSize += ((ByteBuffer)message).capacity();
+ }
+ });
+ client.getSessionConfig().setSslContext(createSSLContext());
+ client.connect(new InetSocketAddress(port));
+
+ }
+ assertTrue(counter.await(10, TimeUnit.MINUTES));
+ assertArrayEquals(message, bos.toByteArray());
+ } finally {
+ server.unbind();
+ }
+ } finally {
+ bos.close();
+ }
+ }
+
+ @Test
+ public void testSingleByteMessageWithJDKClient() throws IOException, GeneralSecurityException, InterruptedException {
+ testMessage(1, Client.JDK);
+ }
+
+ @Test
+ public void testSingleByteMessageWithMINAClientAfterHandkhake() throws IOException, GeneralSecurityException, InterruptedException {
+ testMessage(1, Client.MINA_AFTER_HANDSHAKE);
+ }
+
+ @Test
+ public void testSingleByteMessageWithMINAClientBeforeHandkhake() throws IOException, GeneralSecurityException, InterruptedException {
+ testMessage(1, Client.MINA_BEFORE_HANDSHAKE);
+ }
+ @Test
+ public void test1KMessageWithJDKClient() throws IOException, GeneralSecurityException, InterruptedException {
+ testMessage(1024, Client.JDK);
+ }
+
+ @Test
+ public void test1KMessageWithMINAClientAfterHandskahe() throws IOException, GeneralSecurityException, InterruptedException {
+ testMessage(1024, Client.MINA_AFTER_HANDSHAKE);
+ }
+
+ @Test
+ public void test1KMessageWithMINAClientBeforeHandskahe() throws IOException, GeneralSecurityException, InterruptedException {
+ testMessage(1024, Client.MINA_BEFORE_HANDSHAKE);
+ }
+
+ @Test
+ public void test1MMessageWithJDKClient() throws IOException, GeneralSecurityException, InterruptedException {
+ testMessage(1024 * 1024, Client.JDK);
+ }
+
+ @Test
+ public void test1MMessageWithMINAClientAfterHandshake() throws IOException, GeneralSecurityException, InterruptedException {
+ testMessage(1024 * 1024, Client.MINA_AFTER_HANDSHAKE);
+ }
+
+ @Test
+ public void test1MMessageWithMINAClientBeforeHandshake() throws IOException, GeneralSecurityException, InterruptedException {
+ testMessage(1024 * 1024, Client.MINA_BEFORE_HANDSHAKE);
}
}