NioSslEngine implements NioFilter { @Override public ByteBuffer readAtLeast(SocketChannel channel, int bytes, ByteBuffer wrappedBuffer) throws IOException { if (peerAppData.capacity() > bytes) { if (peerAppData.capacity() - peerAppData.position() < bytes) { peerAppData.compact(); peerAppData.flip(); } } while (peerAppData.remaining() < bytes) { wrappedBuffer.limit(wrappedBuffer.capacity()); int amountRead = channel.read(wrappedBuffer); if (amountRead < 0) { throw new EOFException(); } if (amountRead > 0) { wrappedBuffer.flip(); peerAppData.compact(); peerAppData = unwrap(wrappedBuffer); peerAppData.flip(); } } return peerAppData; } NioSslEngine(SSLEngine engine, BufferPool bufferPool); boolean handshake(SocketChannel socketChannel, int timeout,
      ByteBuffer peerNetData); @Override synchronized ByteBuffer wrap(ByteBuffer appData); @Override synchronized ByteBuffer unwrap(ByteBuffer wrappedBuffer); @Override ByteBuffer ensureWrappedCapacity(int amount, ByteBuffer wrappedBuffer,
      BufferType bufferType); @Override ByteBuffer readAtLeast(SocketChannel channel, int bytes,
      ByteBuffer wrappedBuffer); @Override ByteBuffer getUnwrappedBuffer(ByteBuffer wrappedBuffer); ByteBuffer ensureUnwrappedCapacity(int amount); @Override void doneReadingDirectAck(ByteBuffer unwrappedBuffer); @Override synchronized void close(SocketChannel socketChannel);  }
@Test public void readAtLeast() throws Exception { final int amountToRead = 150; final int individualRead = 60; final int preexistingBytes = 10; ByteBuffer wrappedBuffer = ByteBuffer.allocate(1000); SocketChannel mockChannel = mock(SocketChannel.class); ByteBuffer unwrappedBuffer = nioSslEngine.peerAppData; unwrappedBuffer.position(unwrappedBuffer.capacity() - individualRead); unwrappedBuffer.limit(unwrappedBuffer.position() + preexistingBytes); when(mockChannel.read(any(ByteBuffer.class))).thenAnswer(new Answer<Integer>() { @Override public Integer answer(InvocationOnMock invocation) throws Throwable { ByteBuffer buffer = invocation.getArgument(0); buffer.position(buffer.position() + individualRead); return individualRead; } }); TestSSLEngine testSSLEngine = new TestSSLEngine(); testSSLEngine.addReturnResult(new SSLEngineResult(OK, NEED_UNWRAP, 0, 0)); nioSslEngine.engine = testSSLEngine; ByteBuffer data = nioSslEngine.readAtLeast(mockChannel, amountToRead, wrappedBuffer); verify(mockChannel, times(3)).read(isA(ByteBuffer.class)); assertThat(data.position()).isEqualTo(0); assertThat(data.limit()).isEqualTo(individualRead * 3 + preexistingBytes); }
@Test public void readAtLeastUsingSmallAppBuffer() throws Exception { final int amountToRead = 150; final int individualRead = 60; final int preexistingBytes = 10; ByteBuffer wrappedBuffer = ByteBuffer.allocate(1000); SocketChannel mockChannel = mock(SocketChannel.class); int initialUnwrappedBufferSize = 100; ByteBuffer unwrappedBuffer = ByteBuffer.allocate(initialUnwrappedBufferSize); unwrappedBuffer.position(7).limit(preexistingBytes + 7); nioSslEngine.peerAppData = unwrappedBuffer; when(mockChannel.read(any(ByteBuffer.class))).thenAnswer(new Answer<Integer>() { @Override public Integer answer(InvocationOnMock invocation) throws Throwable { ByteBuffer buffer = invocation.getArgument(0); buffer.position(buffer.position() + individualRead); return individualRead; } }); TestSSLEngine testSSLEngine = new TestSSLEngine(); testSSLEngine.addReturnResult( new SSLEngineResult(BUFFER_OVERFLOW, NEED_UNWRAP, 0, 0), new SSLEngineResult(OK, NEED_UNWRAP, 0, 0), new SSLEngineResult(OK, NEED_UNWRAP, 0, 0), new SSLEngineResult(OK, NEED_UNWRAP, 0, 0)); nioSslEngine.engine = testSSLEngine; ByteBuffer data = nioSslEngine.readAtLeast(mockChannel, amountToRead, wrappedBuffer); verify(mockChannel, times(3)).read(isA(ByteBuffer.class)); assertThat(data.position()).isEqualTo(0); assertThat(data.limit()).isEqualTo(individualRead * 3 + preexistingBytes); int initialFreeSpace = initialUnwrappedBufferSize - preexistingBytes; assertThat(nioSslEngine.peerAppData.capacity()) .isEqualTo(2 * initialFreeSpace + preexistingBytes); }
@Test public void readAtLeastUsingSmallAppBufferAtWriteLimit() throws Exception { final int amountToRead = 150; final int individualRead = 150; int initialUnwrappedBufferSize = 100; final int preexistingBytes = initialUnwrappedBufferSize - 7; ByteBuffer wrappedBuffer = ByteBuffer.allocate(1000); SocketChannel mockChannel = mock(SocketChannel.class); ByteBuffer unwrappedBuffer = ByteBuffer.allocate(initialUnwrappedBufferSize); unwrappedBuffer.position(7).limit(preexistingBytes + 7); nioSslEngine.peerAppData = unwrappedBuffer; when(mockChannel.read(any(ByteBuffer.class))).thenAnswer(new Answer<Integer>() { @Override public Integer answer(InvocationOnMock invocation) throws Throwable { ByteBuffer buffer = invocation.getArgument(0); buffer.position(buffer.position() + individualRead); return individualRead; } }); TestSSLEngine testSSLEngine = new TestSSLEngine(); testSSLEngine.addReturnResult( new SSLEngineResult(BUFFER_OVERFLOW, NEED_UNWRAP, 0, 0), new SSLEngineResult(BUFFER_OVERFLOW, NEED_UNWRAP, 0, 0), new SSLEngineResult(BUFFER_OVERFLOW, NEED_UNWRAP, 0, 0), new SSLEngineResult(OK, NEED_UNWRAP, 0, 0)); nioSslEngine.engine = testSSLEngine; ByteBuffer data = nioSslEngine.readAtLeast(mockChannel, amountToRead, wrappedBuffer); verify(mockChannel, times(1)).read(isA(ByteBuffer.class)); assertThat(data.position()).isEqualTo(0); assertThat(data.limit()) .isEqualTo(individualRead * testSSLEngine.getNumberOfUnwraps() + preexistingBytes); }