/*
* 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.assertTrue;
import java.io.IOError;
import java.io.IOException;
import java.nio.ByteBuffer;
import javax.net.ssl.SSLSession;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.integration.junit4.JUnit4Mockery;
import org.jmock.lib.concurrent.Synchroniser;
import org.junit.Test;
import org.xnio.BufferAllocator;
import org.xnio.ByteBufferSlicePool;
import org.xnio.Pool;
import org.xnio.channels.AssembledConnectedSslStreamChannel;
import org.xnio.channels.ConnectedSslStreamChannel;
import org.xnio.mock.ConnectedStreamChannelMock;
import org.xnio.ssl.mock.SSLEngineMock;
import org.xnio.ssl.mock.SSLEngineMock.HandshakeAction;
/**
* Tests {@link AssembledConnectedSslStreamChannel} in buffer overflow scenarios.
*
* @author <a href="mailto:frainone@redhat.com">Flavia Rainone</a>
*
*/
public class ConnectedSslStreamChannelBufferOverflowTestCase extends AbstractConnectedSslStreamChannelTest {
// mockery context
protected Mockery context;
// the underlying channel used by JsseConnectedSslStreamChannel above
protected ConnectedStreamChannelMock connectedChannelMock;
// the SSLEngine mock, allows to test different engine behavior with channel
protected SSLEngineMock engineMock;
@Override
protected ConnectedSslStreamChannel createSslChannel() {
context = new JUnit4Mockery() {{
setThreadingPolicy(new Synchroniser());
}};
connectedChannelMock = new ConnectedStreamChannelMock();
engineMock = new SSLEngineMockSmallPacketSize(context);
final Pool<ByteBuffer> socketBufferPool = new ByteBufferSlicePool(BufferAllocator.BYTE_BUFFER_ALLOCATOR, 10, 160);
final Pool<ByteBuffer> applicationBufferPool = new ByteBufferSlicePool(BufferAllocator.BYTE_BUFFER_ALLOCATOR, 10, 160);
final SslConnection connection = new JsseSslConnection(connectionMock, engineMock, socketBufferPool, applicationBufferPool);
try {
connection.startHandshake();
} catch (IOException e) {
throw new IOError(e);
}
return new AssembledConnectedSslStreamChannel(connection, connection.getSourceChannel(), connection.getSinkChannel());
}
@Test
public void bufferOverflowOnWrite() throws IOException {
boolean fail = false;
final ByteBuffer buffer = ByteBuffer.allocate(100);
buffer.put("12345678901234567890".getBytes("UTF-8")).flip();
// attempt to write... channel is expected to write to throw an IOException
// SSLEngine required a bigger send buffer but out buffer was already big enough
try {
sslChannel.write(buffer);
} catch (IOException e) {
fail = true;
}
assertTrue(fail);
}
@Test
public void bufferOverflowOnRead() throws IOException {
engineMock.setHandshakeActions(HandshakeAction.NEED_UNWRAP, HandshakeAction.FINISH);
connectedChannelMock.setReadData(SSLEngineMock.HANDSHAKE_MSG);
connectedChannelMock.enableRead(true);
// attempt to read... channel is expected to not be able to read the message because HANDSHAKE_MSG does not fit
// in a 10 byte buffer
assertEquals(0, sslChannel.read(ByteBuffer.allocate(50)));
}
private static class SSLEngineMockSmallPacketSize extends SSLEngineMock {
public SSLEngineMockSmallPacketSize (Mockery mockery) {
super(mockery);
}
@Override
public SSLSession getSession() {
synchronized (mockery) {
final SSLSession sessionMock = mockery.mock(SSLSession.class, "SmallPacketsSession");
mockery.checking(new Expectations() {{
oneOf(sessionMock).getPacketBufferSize();
will(returnValue(10));
oneOf(sessionMock).getApplicationBufferSize();
will(returnValue(5));
}});
return sessionMock;
}
}
}
}