package org.webpieces.nio.test;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.webpieces.util.logging.Logger;
import javax.net.ssl.SSLEngine;
import junit.framework.Assert;
import junit.framework.TestCase;
import org.webpieces.nio.api.deprecated.ChannelServiceFactory;
import org.webpieces.nio.api.libs.AsyncSSLEngine;
import org.webpieces.nio.api.libs.AsyncSSLEngineException;
import org.webpieces.nio.api.libs.BufferHelper;
import org.webpieces.nio.api.libs.FactoryCreator;
import org.webpieces.nio.api.libs.SSLEngineFactory;
import org.webpieces.nio.api.libs.SSLListener;
import org.webpieces.nio.api.testutil.HandlerForTests;
import org.webpieces.nio.api.testutil.MockSSLEngineFactory;
import biz.xsoftware.mock.MockObject;
/**
* Normally I would not separate out one class for testing, but when this
* is integrated with the ChanMgr, testing becomes non-deterministic with
* packets being separated and such. This allows more deterministic
* testing to fully test AsynchSSLEngine.
*
* @author dean.hiller
*/
public class TestNewAsynchSSLEngine extends TestCase {
private static final Logger log = LoggerFactory.getLogger(TestNewAsynchSSLEngine.class);
private BufferHelper helper = ChannelServiceFactory.bufferHelper(null);
private MockSslListener serverList = new MockSslListener();
private MockSslListener clientList = new MockSslListener();
private AsyncSSLEngine serverEngine;
private AsyncSSLEngine clientEngine;
@Override
protected void setUp() throws Exception {
HandlerForTests.setupLogging();
SSLEngineFactory sslEngineFactory = new MockSSLEngineFactory();
FactoryCreator creator = FactoryCreator.createFactory(null);
SSLEngine wrappedSvr = sslEngineFactory.createEngineForServerSocket();
serverEngine = creator.createSSLEngine("[serverAsynch] ", wrappedSvr, null);
serverEngine.setListener((SSLListener)serverList);
SSLEngine wrappedClient = sslEngineFactory.createEngineForSocket();
clientEngine = creator.createSSLEngine("[clientEngine] ", wrappedClient, null);
clientEngine.setListener((SSLListener)clientList);
}
@Override
protected void tearDown() throws Exception {
HandlerForTests.checkForWarnings();
}
public void testBasic() throws Exception {
log.trace("B*******************************************");
clientEngine.beginHandshake();
ByteBuffer b = clientList.getPacketEncrypted();
serverEngine.feedEncryptedPacket(b, null);
Runnable r = serverList.getRunnable();
r.run();
b = serverList.getPacketEncrypted();
clientEngine.feedEncryptedPacket(b, null);
r = clientList.getRunnable();
r.run();
ByteBuffer b0 = clientList.getPacketEncrypted();
serverEngine.feedEncryptedPacket(b0, null);
ByteBuffer b1 = clientList.getPacketEncrypted();
r = serverList.getRunnable();
r.run();
serverEngine.feedEncryptedPacket(b1, null);
ByteBuffer b2 = clientList.getPacketEncrypted();
serverEngine.feedEncryptedPacket(b2, null);
Assert.assertTrue(serverList.isEncryptedLinkEstablished());
b0 = serverList.getPacketEncrypted();
clientEngine.feedEncryptedPacket(b0, null);
b1 = serverList.getPacketEncrypted();
clientEngine.feedEncryptedPacket(b1, null);
Assert.assertTrue(clientList.isEncryptedLinkEstablished());
feedData(clientEngine, clientList, serverEngine, serverList);
feedData(serverEngine, serverList, clientEngine, clientList);
}
/**
* Test case where there is one and one half packets in the buffer, so the second
* one gets a underflow and the buffer was not being reset properly. This test
* proved a bug while all other tests passed so don't delete this as it is not
* a duplicate test case.
*
* Test one and half packets should have caught this same bug but did not for
* some reason.
*
* @throws Exception
*/
public void testLargePackets() throws Exception {
testBasic();
ByteBuffer b = ByteBuffer.allocate(5000);
String payload = "hello";
for(int i = 0; i < 3000; i++) {
payload+="i";
}
helper.putString(b, payload);
helper.doneFillingBuffer(b);
clientEngine.feedPlainPacket(b, null);
ByteBuffer encData1 = clientList.getPacketEncrypted();
log.info("data="+encData1);
b.rewind();
clientEngine.feedPlainPacket(b, null);
ByteBuffer encData2 = clientList.getPacketEncrypted();
log.info("data="+encData2);
b.rewind();
clientEngine.feedPlainPacket(b, null);
ByteBuffer encData3 = clientList.getPacketEncrypted();
log.info("data="+encData3);
ByteBuffer all = ByteBuffer.allocate(10000);
all.clear();
all.put(encData1);
all.put(encData2);
all.put(encData3);
all.position(0);
all.limit(3736);
serverEngine.feedEncryptedPacket(all, null);
Assert.assertNotNull(serverList.getPacketUnencrypted());
all.limit(8736);
all.position(3736);
serverEngine.feedEncryptedPacket(all, null);
Assert.assertNotNull(serverList.getPacketUnencrypted());
}
public void testClientClose() throws Exception {
testBasic();
clientEngine.initiateClose();
ByteBuffer tmp = ByteBuffer.allocate(10);
helper.putString(tmp, "asdf");
helper.doneFillingBuffer(tmp);
try {
clientEngine.feedPlainPacket(tmp, null);
fail("should have thrown exception");
} catch(IllegalStateException e) {}
ByteBuffer b = clientList.getPacketEncrypted();
serverEngine.feedEncryptedPacket(b, null);
b = serverList.getPacketEncrypted();
Assert.assertTrue(serverList.isClosed());
clientEngine.feedEncryptedPacket(b, null);
Assert.assertTrue(clientList.isClosed());
}
public void testServerClose() throws Exception {
testBasic();
serverEngine.initiateClose();
ByteBuffer b = serverList.getPacketEncrypted();
clientEngine.feedEncryptedPacket(b, null);
b = clientList.getPacketEncrypted();
Assert.assertTrue(clientList.isClosed());
serverEngine.feedEncryptedPacket(b, null);
Assert.assertTrue(serverList.isClosed());
}
static void closeWithExpects(AsyncSSLEngine engine, MockObject sslListener) throws IOException {
engine.close();
String[] methodNames = new String[2];
methodNames[0] = "packetEncrypted";
methodNames[1] = "closed";
sslListener.expect(methodNames);
}
static void feedPacket(AsyncSSLEngine engine, ByteBuffer b) throws Exception {
int bytesLeft = 0;
if(b.remaining() < 15)
bytesLeft = 2;
else
bytesLeft = 12;
int lim = b.limit();
b.limit(bytesLeft);
engine.feedEncryptedPacket(b, null);
b.limit(lim);
engine.feedEncryptedPacket(b, null);
}
/**
* Two tests in one. Test Runnable not finished on rehandshake and
* feeding packets. Then test rehandshake failure.
*
* The end of this test is kind of weird. I thought a close handshake could be
* processed in the middle of a handshake to close the engine, but it appears
* this does not work.
*
* @throws Exception
*/
public void testRunnableNotFinishedAndHandshakeFailure() throws Exception {
testBasic();
feedData(clientEngine, clientList, serverEngine, serverList);
log.trace("B*******************************************");
clientEngine.beginHandshake();
String expected = "abcasdfaasdfsdfsfsfdsfs";
ByteBuffer data = ByteBuffer.allocate(100);
helper.putString(data, expected);
helper.doneFillingBuffer(data);
clientEngine.feedPlainPacket(data, null);
ByteBuffer b = clientList.getPacketEncrypted();
ByteBuffer encData = clientList.getPacketEncrypted();
serverEngine.feedEncryptedPacket(b, null);
Runnable r = serverList.getRunnable();
try {
serverEngine.feedEncryptedPacket(encData, null);
fail("Should have thrown exception since Runnable has not completed yet");
} catch(IllegalStateException e) {}
try {
data.rewind();
serverEngine.feedPlainPacket(data, null);
fail("Should have thrown exception since Runnable has not completed yet");
} catch(IllegalStateException e) {}
r.run();
b = serverList.getPacketEncrypted();
try {
clientEngine.feedEncryptedPacket(data, null);
fail("Should have thrown exception");
} catch(AsyncSSLEngineException e) {}
b = clientList.getPacketEncrypted();
Assert.assertTrue(clientList.isClosed());
try {
serverEngine.feedEncryptedPacket(b, null);
fail("not sure why this throws exceptoin...I expected it to be able to take a close handshake message");
} catch(AsyncSSLEngineException e) {}
serverList.getPacketEncrypted();
Assert.assertTrue(serverList.isClosed());
}
public void testDataDuringRehandshake() throws Exception {
testBasic();
feedData(clientEngine, clientList, serverEngine, serverList);
log.trace("B*******************************************");
clientEngine.beginHandshake();
String expected = "abc";
ByteBuffer data = ByteBuffer.allocate(10);
helper.putString(data, expected);
helper.doneFillingBuffer(data);
clientEngine.feedPlainPacket(data, null);
ByteBuffer b = clientList.getPacketEncrypted();
ByteBuffer encData = clientList.getPacketEncrypted();
serverEngine.feedEncryptedPacket(b, null);
Runnable r = serverList.getRunnable();
r.run();
ByteBuffer hsMsg = serverList.getPacketEncrypted();
serverEngine.feedEncryptedPacket(encData, null);
b = serverList.getAssembledUnencryptedPacket();
String actual = helper.readString(b, b.remaining());
assertEquals(expected, actual);
clientEngine.feedEncryptedPacket(hsMsg, null);
r = clientList.getRunnable();
r.run();
// feedData(clientEngine, clientList, serverEngine, serverList);
ByteBuffer b0 = clientList.getPacketEncrypted();
// feedData(clientEngine, clientList, serverEngine, serverList);
serverEngine.feedEncryptedPacket(b0, null);
ByteBuffer b1 = clientList.getPacketEncrypted();
r = serverList.getRunnable();
r.run();
serverEngine.feedEncryptedPacket(b1, null);
ByteBuffer b2 = clientList.getPacketEncrypted();
serverEngine.feedEncryptedPacket(b2, null);
b0 = serverList.getPacketEncrypted();
clientEngine.feedEncryptedPacket(b0, null);
b1 = serverList.getPacketEncrypted();
clientEngine.feedEncryptedPacket(b1, null);
// feedData(clientEngine, clientList, serverEngine, serverList);
}
private void feedData(AsyncSSLEngine from, MockSslListener fromList, AsyncSSLEngine to, MockSslListener toList) throws Exception {
String expected = "abc";
ByteBuffer data = ByteBuffer.allocate(10);
helper.putString(data, expected);
helper.doneFillingBuffer(data);
from.feedPlainPacket(data, null);
ByteBuffer encData = fromList.getPacketEncrypted();
to.feedEncryptedPacket(encData, null);
ByteBuffer b = toList.getAssembledUnencryptedPacket();
String actual = helper.readString(b, b.remaining());
assertEquals(expected, actual);
}
/**
* Once close is started, incoming data may still keep coming. Instead of throwing
* exception after exception when getting this data, we should drop it on the floor
* as in many systems it is expected to keep getting data.
*
* @throws Exception
*/
public void testDataDuringClose() throws Exception {
testBasic();
clientEngine.initiateClose();
ByteBuffer data = ByteBuffer.allocate(10);
helper.putString(data, "asdf");
helper.doneFillingBuffer(data);
serverEngine.feedPlainPacket(data, null);
ByteBuffer encData = serverList.getPacketEncrypted();
String expected = "asdf";
ByteBuffer tmp = ByteBuffer.allocate(10);
helper.putString(tmp, expected);
helper.doneFillingBuffer(tmp);
try {
clientEngine.feedPlainPacket(tmp, null);
fail("should have thrown exception");
} catch(IllegalStateException e) {}
ByteBuffer b = clientList.getPacketEncrypted();
serverEngine.feedEncryptedPacket(b, null);
ByteBuffer hsMsg = serverList.getPacketEncrypted();
Assert.assertTrue(serverList.isClosed());
clientEngine.feedEncryptedPacket(encData, null);
b = clientList.getAssembledUnencryptedPacket();
log.info("******buffer="+b);
String actual = helper.readString(b, b.remaining());
assertEquals(expected, actual);
clientEngine.feedEncryptedPacket(hsMsg, null);
Assert.assertTrue(clientList.isClosed());
}
}