diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/SocketChannelEndPointOpenCloseTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/SocketChannelEndPointOpenCloseTest.java new file mode 100644 index 00000000000..3ba75ca4684 --- /dev/null +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/SocketChannelEndPointOpenCloseTest.java @@ -0,0 +1,180 @@ +// +// ======================================================================== +// Copyright (c) 1995-2018 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.io; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.nio.ByteBuffer; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; + +import org.eclipse.jetty.util.BufferUtil; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +public class SocketChannelEndPointOpenCloseTest +{ + public static class EndPointPair + { + public SocketChannelEndPoint client; + public SocketChannelEndPoint server; + } + + static ServerSocketChannel connector; + + @BeforeClass + public static void open() throws Exception + { + connector = ServerSocketChannel.open(); + connector.socket().bind(null); + } + + @AfterClass + public static void close() throws Exception + { + connector.close(); + connector=null; + } + + private EndPointPair newConnection() throws Exception + { + EndPointPair c = new EndPointPair(); + + c.client=new SocketChannelEndPoint(SocketChannel.open(connector.socket().getLocalSocketAddress()),null,null,null); + c.server=new SocketChannelEndPoint(connector.accept(),null,null,null); + return c; + } + + @Test + public void testClientServerExchange() throws Exception + { + EndPointPair c = newConnection(); + ByteBuffer buffer = BufferUtil.allocate(4096); + + // Client sends a request + c.client.flush(BufferUtil.toBuffer("request")); + + // Server receives the request + int len = c.server.fill(buffer); + assertEquals(7,len); + assertEquals("request",BufferUtil.toString(buffer)); + + // Client and server are open + assertTrue(c.client.isOpen()); + assertFalse(c.client.isOutputShutdown()); + assertTrue(c.server.isOpen()); + assertFalse(c.server.isOutputShutdown()); + + // Server sends response and closes output + c.server.flush(BufferUtil.toBuffer("response")); + c.server.shutdownOutput(); + + // client server are open, server is oshut + assertTrue(c.client.isOpen()); + assertFalse(c.client.isOutputShutdown()); + assertTrue(c.server.isOpen()); + assertTrue(c.server.isOutputShutdown()); + + // Client reads response + BufferUtil.clear(buffer); + len = c.client.fill(buffer); + assertEquals(8,len); + assertEquals("response",BufferUtil.toString(buffer)); + + // Client and server are open, server is oshut + assertTrue(c.client.isOpen()); + assertFalse(c.client.isOutputShutdown()); + assertTrue(c.server.isOpen()); + assertTrue(c.server.isOutputShutdown()); + + // Client reads -1 + BufferUtil.clear(buffer); + len = c.client.fill(buffer); + assertEquals(-1,len); + + // Client and server are open, server is oshut, client is ishut + assertTrue(c.client.isOpen()); + assertFalse(c.client.isOutputShutdown()); + assertTrue(c.server.isOpen()); + assertTrue(c.server.isOutputShutdown()); + + // Client shutsdown output, which is a close because already ishut + c.client.shutdownOutput(); + + // Client is closed. Server is open and oshut + assertFalse(c.client.isOpen()); + assertTrue(c.client.isOutputShutdown()); + assertTrue(c.server.isOpen()); + assertTrue(c.server.isOutputShutdown()); + + // Server reads close + BufferUtil.clear(buffer); + len = c.server.fill(buffer); + assertEquals(-1,len); + + // Client and Server are closed + assertFalse(c.client.isOpen()); + assertTrue(c.client.isOutputShutdown()); + assertFalse(c.server.isOpen()); + assertTrue(c.server.isOutputShutdown()); + } + + @Test + public void testClientClose() throws Exception + { + EndPointPair c = newConnection(); + ByteBuffer buffer = BufferUtil.allocate(4096); + + c.client.flush(BufferUtil.toBuffer("request")); + int len = c.server.fill(buffer); + assertEquals(7,len); + assertEquals("request",BufferUtil.toString(buffer)); + + assertTrue(c.client.isOpen()); + assertFalse(c.client.isOutputShutdown()); + assertTrue(c.server.isOpen()); + assertFalse(c.server.isOutputShutdown()); + + c.client.close(); + + assertFalse(c.client.isOpen()); + assertTrue(c.client.isOutputShutdown()); + assertTrue(c.server.isOpen()); + assertFalse(c.server.isOutputShutdown()); + + len = c.server.fill(buffer); + assertEquals(-1,len); + + assertFalse(c.client.isOpen()); + assertTrue(c.client.isOutputShutdown()); + assertTrue(c.server.isOpen()); + assertFalse(c.server.isOutputShutdown()); + + c.server.shutdownOutput(); + + assertFalse(c.client.isOpen()); + assertTrue(c.client.isOutputShutdown()); + assertFalse(c.server.isOpen()); + assertTrue(c.server.isOutputShutdown()); + } + +} diff --git a/jetty-io/src/test/java/org/eclipse/jetty/io/SslEngineBehaviorTest.java b/jetty-io/src/test/java/org/eclipse/jetty/io/SslEngineBehaviorTest.java new file mode 100644 index 00000000000..bb798be2f3b --- /dev/null +++ b/jetty-io/src/test/java/org/eclipse/jetty/io/SslEngineBehaviorTest.java @@ -0,0 +1,145 @@ +// +// ======================================================================== +// Copyright (c) 1995-2018 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.io; + +import static org.hamcrest.Matchers.greaterThan; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; + +import java.io.File; +import java.nio.ByteBuffer; + +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLEngineResult; + +import org.eclipse.jetty.toolchain.test.JDK; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.BufferUtil; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Assume; +import org.junit.BeforeClass; +import org.junit.Test; + +public class SslEngineBehaviorTest +{ + private static SslContextFactory sslCtxFactory; + + @BeforeClass + public static void startSsl() throws Exception + { + File keystore = MavenTestingUtils.getTestResourceFile("keystore"); + sslCtxFactory.setKeyStorePath(keystore.getAbsolutePath()); + sslCtxFactory.setKeyStorePassword("storepwd"); + sslCtxFactory.setKeyManagerPassword("keypwd"); + sslCtxFactory.setEndpointIdentificationAlgorithm(""); + sslCtxFactory.start(); + } + + @AfterClass + public static void stopSsl() throws Exception + { + sslCtxFactory.stop(); + } + + @Test + public void checkSslEngineBehaviour() throws Exception + { + Assume.assumeFalse(JDK.IS_9); + + SSLEngine server = sslCtxFactory.newSSLEngine(); + SSLEngine client = sslCtxFactory.newSSLEngine(); + + ByteBuffer netC2S = ByteBuffer.allocate(server.getSession().getPacketBufferSize()); + ByteBuffer netS2C = ByteBuffer.allocate(server.getSession().getPacketBufferSize()); + ByteBuffer serverIn = ByteBuffer.allocate(server.getSession().getApplicationBufferSize()); + ByteBuffer serverOut = ByteBuffer.allocate(server.getSession().getApplicationBufferSize()); + ByteBuffer clientIn = ByteBuffer.allocate(client.getSession().getApplicationBufferSize()); + + SSLEngineResult result; + + // start the client + client.setUseClientMode(true); + client.beginHandshake(); + Assert.assertEquals(SSLEngineResult.HandshakeStatus.NEED_WRAP,client.getHandshakeStatus()); + + // what if we try an unwrap? + netS2C.flip(); + result=client.unwrap(netS2C,clientIn); + // unwrap is a noop + assertEquals(SSLEngineResult.Status.OK,result.getStatus()); + assertEquals(0,result.bytesConsumed()); + assertEquals(0,result.bytesProduced()); + assertEquals(SSLEngineResult.HandshakeStatus.NEED_WRAP,result.getHandshakeStatus()); + netS2C.clear(); + + // do the needed WRAP of empty buffer + result=client.wrap(BufferUtil.EMPTY_BUFFER,netC2S); + // unwrap is a noop + assertEquals(SSLEngineResult.Status.OK,result.getStatus()); + assertEquals(0,result.bytesConsumed()); + assertThat(result.bytesProduced(),greaterThan(0)); + assertEquals(SSLEngineResult.HandshakeStatus.NEED_UNWRAP,result.getHandshakeStatus()); + netC2S.flip(); + assertEquals(netC2S.remaining(),result.bytesProduced()); + + // start the server + server.setUseClientMode(false); + server.beginHandshake(); + Assert.assertEquals(SSLEngineResult.HandshakeStatus.NEED_UNWRAP,server.getHandshakeStatus()); + + // what if we try a needless wrap? + serverOut.put(BufferUtil.toBuffer("Hello World")); + serverOut.flip(); + result=server.wrap(serverOut,netS2C); + // wrap is a noop + assertEquals(SSLEngineResult.Status.OK,result.getStatus()); + assertEquals(0,result.bytesConsumed()); + assertEquals(0,result.bytesProduced()); + assertEquals(SSLEngineResult.HandshakeStatus.NEED_UNWRAP,result.getHandshakeStatus()); + + // Do the needed unwrap, to an empty buffer + result=server.unwrap(netC2S,BufferUtil.EMPTY_BUFFER); + assertEquals(SSLEngineResult.Status.BUFFER_OVERFLOW,result.getStatus()); + assertEquals(0,result.bytesConsumed()); + assertEquals(0,result.bytesProduced()); + assertEquals(SSLEngineResult.HandshakeStatus.NEED_UNWRAP,result.getHandshakeStatus()); + + // Do the needed unwrap, to a full buffer + serverIn.position(serverIn.limit()); + result=server.unwrap(netC2S,serverIn); + assertEquals(SSLEngineResult.Status.BUFFER_OVERFLOW,result.getStatus()); + assertEquals(0,result.bytesConsumed()); + assertEquals(0,result.bytesProduced()); + assertEquals(SSLEngineResult.HandshakeStatus.NEED_UNWRAP,result.getHandshakeStatus()); + + // Do the needed unwrap, to an empty buffer + serverIn.clear(); + result=server.unwrap(netC2S,serverIn); + assertEquals(SSLEngineResult.Status.OK,result.getStatus()); + assertThat(result.bytesConsumed(),greaterThan(0)); + assertEquals(0,result.bytesProduced()); + assertEquals(SSLEngineResult.HandshakeStatus.NEED_TASK,result.getHandshakeStatus()); + + server.getDelegatedTask().run(); + + assertEquals(SSLEngineResult.HandshakeStatus.NEED_WRAP,server.getHandshakeStatus()); + } +}