/*
* JBoss, Home of Professional Open Source.
*
* Copyright 2013 Red Hat, Inc. and/or its affiliates, and individual
* contributors as indicated by the @author tags.
*
* Licensed 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.xnio.ssl;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.xnio.ssl.mock.SSLEngineMock.CLOSE_MSG;
import static org.xnio.ssl.mock.SSLEngineMock.HANDSHAKE_MSG;
import static org.xnio.ssl.mock.SSLEngineMock.HandshakeAction.FINISH;
import static org.xnio.ssl.mock.SSLEngineMock.HandshakeAction.NEED_FAULTY_TASK;
import static org.xnio.ssl.mock.SSLEngineMock.HandshakeAction.NEED_TASK;
import static org.xnio.ssl.mock.SSLEngineMock.HandshakeAction.NEED_UNWRAP;
import static org.xnio.ssl.mock.SSLEngineMock.HandshakeAction.NEED_WRAP;
import static org.xnio.ssl.mock.SSLEngineMock.HandshakeAction.PERFORM_REQUESTED_ACTION;
import java.io.IOException;
import java.nio.ByteBuffer;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
import org.jmock.integration.junit4.JMock;
import org.jmock.integration.junit4.JUnitRuleMockery;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.xnio.ssl.mock.SSLEngineMock;
/**
* Test for read operations on {@link #JsseSslStreamSourceConduit}.
*
* @author <a href="mailto:frainone@redhat.com">Flavia Rainone</a>
*/
public class JsseSslStreamSourceConduitTestCase extends AbstractSslConnectionTest{
@Rule
public final JUnitRuleMockery context = new JUnitRuleMockery();
@Test
public void readWithoutHandshake() throws IOException {
// no handshake actions for engineMock this time, meaning it will just wrap and unwrap without any handshake
// the message we want to write
conduitMock.setReadData("MockTest", CLOSE_MSG);
conduitMock.enableReads(true);
final ByteBuffer buffer = ByteBuffer.allocate(100);
// attempt to read... sourceConduit is expected to read the entire message without any issues
assertEquals(8, sourceConduit.read(buffer));
// terminate reads
sourceConduit.terminateReads();
// close connection
sinkConduit.terminateWrites();
// data expected to have been copied to buffer by sourceConduit
assertReadMessage(buffer, "MockTest");
}
@Test
public void readWithoutHandshakeMappedUnwrap() throws IOException {
// map the wrap
engineMock.addWrapEntry("MockTest", "WRAPPED_MOCK_TEST");
// no handshake actions for engineMock this time, meaning it will just wrap and unwrap without any handshake
// the message we want to read
conduitMock.setReadData("WRAPPED_MOCK_TEST", CLOSE_MSG);
conduitMock.enableReads(true);
// attempt to read... conduit is expected to read the entire message without any handshake issues
final ByteBuffer buffer = ByteBuffer.allocate(100);
assertEquals(8, sourceConduit.read(buffer));
// terminate reads
sourceConduit.terminateReads();
// close connection
sinkConduit.terminateWrites();
// data expected to have been read to 'buffer' by 'sourceConduit'
assertReadMessage(buffer, "MockTest");
}
@Test
public void readWithSimpleHandshake() throws IOException {
// map all data to be read and written
engineMock.addWrapEntry(HANDSHAKE_MSG, "handshake");
engineMock.addWrapEntry("MockTest", "mock test works!");
engineMock.addWrapEntry(CLOSE_MSG, "channel closed");
// set the handshake actions that engineMock will emulate
engineMock.setHandshakeActions(NEED_WRAP, NEED_UNWRAP, NEED_TASK, FINISH);
// set ReadData on conduitMock, including the wrapped version of message we want to read
conduitMock.setReadData("handshake", "mock test works!", "channel closed");
conduitMock.enableReads(true);
// conduit is expected to read all data from conduitMock
final ByteBuffer buffer = ByteBuffer.allocate(100);
assertEquals(8, sourceConduit.read(buffer));
// terminate reads
sourceConduit.terminateReads();
// close connection
sinkConduit.terminateWrites();
assertReadMessage(buffer, "MockTest");
// data expected to have been written to 'conduitMock' by 'sourceConduit'
assertWrittenMessage("handshake", "channel closed");
}
@Test
public void readWithTasklessHandshake() throws IOException {
// map data to be read and written
engineMock.addWrapEntry("Mock Test", "{testReadWithTasklessHandshake}");
engineMock.addWrapEntry(CLOSE_MSG, " _ ");
// set the handshake actions that engineMock will emulate
engineMock.setHandshakeActions(NEED_WRAP, NEED_UNWRAP, FINISH);
// set ReadData on conduitMock
conduitMock.setReadData(SSLEngineMock.HANDSHAKE_MSG, "{testReadWithTasklessHandshake}", " _ ");
conduitMock.enableReads(true);
// conduit is expected to read "Mock Test" from conduitMock
final ByteBuffer buffer = ByteBuffer.allocate(100);
assertEquals(9, sourceConduit.read(buffer));
// conduit should be able to terminate reads
sourceConduit.terminateReads();
// close connection
sinkConduit.terminateWrites();
// data expected to have been read from 'conduitMock' by 'sourceConduit'
assertReadMessage(buffer, "Mock Test");
// data expected to have been written to 'conduitMock' by 'sinkConduit'
assertWrittenMessage(HANDSHAKE_MSG, " _ ");
}
@Test
public void multipleReadsWithSimpleHandshake() throws IOException {
// map data to be read and written
engineMock.addWrapEntry(HANDSHAKE_MSG, "{handshake data}");
engineMock.addWrapEntry("Mock Read Test", "{data}");
engineMock.addWrapEntry("it works!", "{more data}");
engineMock.addWrapEntry(CLOSE_MSG, "{message closed}");
// set the handshake actions that engineMock will emulate
engineMock.setHandshakeActions(NEED_WRAP, NEED_UNWRAP, NEED_TASK, FINISH);
// set ReadData on conduitMock
conduitMock.setReadData("{handshake data}", "{data}", "{data}", "{more data}");
// enable read on conduitMock, meaning that data above will be available to be read right away
conduitMock.enableReads(true);
// try to read message
final ByteBuffer buffer = ByteBuffer.allocate(30);
assertEquals(30, sourceConduit.read(buffer));
// data expected to have been read from 'conduitMock' by 'sourceConduit' so far
assertReadMessage(buffer, "Mock Read Test", "Mock Read Test", "it");
buffer.clear();
// set more read data
conduitMock.setReadData("{data}", "{more data}");
conduitMock.enableReads(true);
// and read again
assertEquals(30, sourceConduit.read(buffer));
// data expected to have been read from 'conduitMock' by 'sourceConduit'
assertReadMessage(buffer, " works!", "Mock Read Test", "it works!");
buffer.clear();
// set more read data
conduitMock.setReadData("{more data}", "{message closed}");
conduitMock.enableReads(true);
// and read again
assertEquals(9, sourceConduit.read(buffer));
// data expected to have been read from 'conduitMock' by 'sourceConduit'
assertReadMessage(buffer, "it works!");
buffer.clear();
// conduit should be able to terminate reads
sourceConduit.terminateReads();
// close connection
sinkConduit.terminateWrites();
// data expected to have been written to 'conduitMock' by 'sinkConduit'
assertWrittenMessage("{handshake data}", "{message closed}");
}
@Test
public void readWithAbruptClose() throws IOException {
// set the handshake actions that engineMock will emulate
engineMock.setHandshakeActions(NEED_UNWRAP, NEED_WRAP, NEED_UNWRAP, NEED_WRAP, NEED_TASK, FINISH);
// message buffer
final ByteBuffer buffer = ByteBuffer.allocate(100);
conduitMock.setReadData(HANDSHAKE_MSG, CLOSE_MSG);
conduitMock.enableReads(true);
// attempt to read several times... conduit is expected to stop on the second NEED_UNWRAP action, as
// conduitMock will read an abrupt CLOSE_MSG
assertEquals(-1, sourceConduit.read(buffer));
assertEquals(-1, sourceConduit.read(buffer));
assertEquals(-1, sourceConduit.read(buffer));
// conduit should be able to terminate writes
sourceConduit.terminateReads();
// close connection
sinkConduit.terminateWrites();
// data expected to have been written to 'conduitMock' by 'sinkConduit'
assertWrittenMessage(HANDSHAKE_MSG, CLOSE_MSG);
}
@Test
public void readWithConstantHandshake() throws IOException {
// set the handshake actions that engineMock will emulate
engineMock.setHandshakeActions(NEED_WRAP, NEED_UNWRAP, NEED_TASK, FINISH, PERFORM_REQUESTED_ACTION,
NEED_UNWRAP, NEED_WRAP, NEED_TASK, FINISH, PERFORM_REQUESTED_ACTION,
NEED_WRAP, NEED_WRAP, NEED_WRAP, NEED_UNWRAP, NEED_TASK, NEED_TASK, NEED_TASK, NEED_TASK, FINISH,
PERFORM_REQUESTED_ACTION, NEED_WRAP, NEED_UNWRAP, NEED_TASK, FINISH, PERFORM_REQUESTED_ACTION);
// set ReadData on conduitMock
conduitMock.setReadData(HANDSHAKE_MSG, "read a lot", HANDSHAKE_MSG, " read a lot ",
HANDSHAKE_MSG, "read a lot ", HANDSHAKE_MSG, " read a lot", CLOSE_MSG);
// enable read on conduitMock, meaning that data above will be available to be read right away
conduitMock.enableReads(true);
// try to read message
final ByteBuffer buffer = ByteBuffer.allocate(10);
assertEquals(10, sourceConduit.read(buffer));
assertReadMessage(buffer, "read a lot");
buffer.clear();
assertEquals(10, sourceConduit.read(buffer));
assertReadMessage(buffer, " read a lo");
buffer.clear();
assertEquals(2, sourceConduit.read(buffer));
// tasks
sourceConduit.awaitReadable();
assertEquals(8, sourceConduit.read(buffer));
assertReadMessage(buffer, "t read a l");
buffer.clear();
assertEquals(3, sourceConduit.read(buffer));
// tasks
sourceConduit.awaitReadable();
assertEquals(7, sourceConduit.read(buffer));
assertReadMessage(buffer, "ot read a");
buffer.clear();
assertEquals(4, sourceConduit.read(buffer));
assertReadMessage(buffer, " lot");
// make sure that conduit managed to do the WRAP and there is no more handshake actions left
assertSame(HandshakeStatus.NOT_HANDSHAKING, engineMock.getHandshakeStatus());
// terminate reads
sourceConduit.terminateReads();
// close connection
sinkConduit.terminateWrites();
// data expected to have been written to 'conduitMock' by 'sinkConduit'
assertWrittenMessage(HANDSHAKE_MSG, HANDSHAKE_MSG, HANDSHAKE_MSG, HANDSHAKE_MSG, HANDSHAKE_MSG, HANDSHAKE_MSG,
CLOSE_MSG);
}
@Test
public void readWithConstantHandshakeAndMappedData() throws IOException {
// map data to be read and written
engineMock.addWrapEntry(HANDSHAKE_MSG, "HANDSHAKE_MSG");
engineMock.addWrapEntry("a lot", "read a lot");
engineMock.addWrapEntry("read", "read");
engineMock.addWrapEntry("lot", "a lot");
engineMock.addWrapEntry("nothing", "read nothing");
engineMock.addWrapEntry(CLOSE_MSG, "CLOSE_MSG");
// set the handshake actions that engineMock will emulate
engineMock.setHandshakeActions(NEED_WRAP, NEED_UNWRAP, NEED_TASK, FINISH, PERFORM_REQUESTED_ACTION,
NEED_UNWRAP, NEED_WRAP, NEED_TASK, FINISH, PERFORM_REQUESTED_ACTION,
NEED_WRAP, NEED_WRAP, NEED_WRAP, NEED_UNWRAP, NEED_TASK, NEED_TASK, NEED_TASK, NEED_TASK, FINISH,
PERFORM_REQUESTED_ACTION, NEED_WRAP, NEED_UNWRAP, NEED_TASK, FINISH, PERFORM_REQUESTED_ACTION);
// set ReadData on conduitMock
conduitMock.setReadData("HANDSHAKE_MSG", "read a lot", "a lot", "read", "HANDSHAKE_MSG", "a lot",
"read a lot", "HANDSHAKE_MSG", "read nothing", "read a lot", "HANDSHAKE_MSG", "read nothing",
"CLOSE_MSG");
// enable read on conduitMock, meaning that data above will be available to be read right away
conduitMock.enableReads(true);
// try to read message
final ByteBuffer buffer = ByteBuffer.allocate(10);
assertEquals(10, sourceConduit.read(buffer));
assertReadMessage(buffer, "a lot", "lot", "re");
buffer.clear();
assertEquals(2, sourceConduit.read(buffer));
sourceConduit.awaitReadable();
assertEquals(8, sourceConduit.read(buffer));
assertReadMessage(buffer, "ad", "lot", "a lot");
buffer.clear();
assertEquals(10, sourceConduit.read(buffer));
assertReadMessage(buffer, "nothing", "a l");
buffer.clear();
assertEquals(2, sourceConduit.read(buffer));
sourceConduit.awaitReadable();
assertEquals(7, sourceConduit.read(buffer));
assertReadMessage(buffer, "ot", "nothing");
buffer.clear();
// make sure that conduit managed to do the WRAP and there is no more handshake actions left
assertSame(HandshakeStatus.NOT_HANDSHAKING, engineMock.getHandshakeStatus());
// terminate reads
sourceConduit.terminateReads();
// close connection
sinkConduit.terminateWrites();
// data expected to have been written to 'conduitMock' by 'sinkConduit'
assertWrittenMessage("HANDSHAKE_MSG", "HANDSHAKE_MSG", "HANDSHAKE_MSG", "HANDSHAKE_MSG", "HANDSHAKE_MSG",
"HANDSHAKE_MSG", "CLOSE_MSG");
}
@Test
public void readWithIntercalatedHandshake() throws IOException {
// set the handshake actions that engineMock will emulate
engineMock.setHandshakeActions(NEED_WRAP, NEED_UNWRAP, NEED_TASK, FINISH, PERFORM_REQUESTED_ACTION,
NEED_WRAP, NEED_UNWRAP, PERFORM_REQUESTED_ACTION, NEED_UNWRAP, PERFORM_REQUESTED_ACTION, FINISH);
// set ReadData on conduitMock
conduitMock.setReadData(HANDSHAKE_MSG, "read this", "read this", "read this", "read this",
HANDSHAKE_MSG, "read this", HANDSHAKE_MSG, "read this", CLOSE_MSG);
// enable read on conduitMock, meaning that data above will be available to be read right away
conduitMock.enableReads(true);
final ByteBuffer buffer = ByteBuffer.allocate(100);
// attempt to read... conduit is expected to read the message
assertEquals(54, sourceConduit.read(buffer));
// make sure that conduit managed to do the WRAP and there is no more handshake actions left
assertSame(HandshakeStatus.NOT_HANDSHAKING, engineMock.getHandshakeStatus());
// terminate reads
sourceConduit.terminateReads();
// close connection
sinkConduit.terminateWrites();
// data expected to have been read from 'conduitMock' by 'sourceConduit'
assertReadMessage(buffer, "read this", "read this", "read this", "read this", "read this", "read this");
// data expected to have been written to 'conduitMock' by 'sinkConduit'
assertWrittenMessage(HANDSHAKE_MSG, HANDSHAKE_MSG, CLOSE_MSG);
}
@Test
public void readWithIntercalatedHandshakeAndMappedData() throws IOException {
// map all data to be read and written
engineMock.addWrapEntry(HANDSHAKE_MSG, "[!@#$%^&*()_]");
engineMock.addWrapEntry("this", "read this");
engineMock.addWrapEntry(CLOSE_MSG, "[_)(*&^%$#@!]");
// set the handshake actions that engineMock will emulate
engineMock.setHandshakeActions(NEED_WRAP, NEED_UNWRAP, NEED_TASK, FINISH, PERFORM_REQUESTED_ACTION,
NEED_WRAP, NEED_UNWRAP, PERFORM_REQUESTED_ACTION, NEED_UNWRAP, PERFORM_REQUESTED_ACTION, FINISH);
// set ReadData on conduitMock
conduitMock.setReadData("[!@#$%^&*()_]", "read this", "read this", "read this", "read this",
"[!@#$%^&*()_]", "read this", "[!@#$%^&*()_]", "read this", "[_)(*&^%$#@!]");
// enable read on conduitMock, meaning that data above will be available to be read right away
conduitMock.enableReads(true);
final ByteBuffer buffer = ByteBuffer.allocate(100);
// attempt to read... conduit is expected to read the message
assertEquals(24, sourceConduit.read(buffer));
// make sure that conduit managed to do the WRAP and there is no more handshake actions left
assertSame(HandshakeStatus.NOT_HANDSHAKING, engineMock.getHandshakeStatus());
// terminate reads
sourceConduit.terminateReads();
// close connection
sinkConduit.terminateWrites();
// data expected to have been read from 'conduitMock' by 'sourceConduit'
assertReadMessage(buffer, "this", "this", "this", "this", "this", "this");
// data expected to have been written to 'conduitMock' by 'sinkConduit'
assertWrittenMessage("[!@#$%^&*()_]", "[!@#$%^&*()_]", "[_)(*&^%$#@!]");
}
@Test
public void attemptToReadWithFaultyTask() throws IOException {
// set the handshake actions that engineMock will emulate
engineMock.setHandshakeActions(NEED_FAULTY_TASK);
// message we plan to write
final ByteBuffer buffer = ByteBuffer.allocate(21);
// try to write a bunch of times, we will get an IOException at all of the times
for (int i = 0; i < 10; i ++) {
boolean failed = false;
try {
sourceConduit.read(buffer);
} catch (IOException expected) {
failed = true;
}
assertTrue(failed);
}
}
}