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:38 UTC
[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
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();
}
}