Repository: mina Updated Branches: refs/heads/2.0 eee4c5a5a -> 478d25139
Added tests for issues DIRMINA-1076 and DIRMINA-1077. Project: http://git-wip-us.apache.org/repos/asf/mina/repo Commit: http://git-wip-us.apache.org/repos/asf/mina/commit/4f9b2c60 Tree: http://git-wip-us.apache.org/repos/asf/mina/tree/4f9b2c60 Diff: http://git-wip-us.apache.org/repos/asf/mina/diff/4f9b2c60 Branch: refs/heads/2.0 Commit: 4f9b2c609aff491702619258babd9ce51dbc1b11 Parents: 3433440 Author: chrjohn <christoph.j...@macd.com> Authored: Sat Mar 3 22:58:53 2018 +0100 Committer: chrjohn <christoph.j...@macd.com> Committed: Sat Mar 3 22:58:53 2018 +0100 ---------------------------------------------------------------------- .../AbstractIoServiceDIRMINA1076Test.java | 172 +++++++++++++++++ ...slTestHandshakeExceptionDIRMINA1077Test.java | 186 +++++++++++++++++++ .../mina/filter/ssl/emptykeystore.sslTest | Bin 0 -> 32 bytes 3 files changed, 358 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mina/blob/4f9b2c60/mina-core/src/test/java/org/apache/mina/core/service/AbstractIoServiceDIRMINA1076Test.java ---------------------------------------------------------------------- diff --git a/mina-core/src/test/java/org/apache/mina/core/service/AbstractIoServiceDIRMINA1076Test.java b/mina-core/src/test/java/org/apache/mina/core/service/AbstractIoServiceDIRMINA1076Test.java new file mode 100644 index 0000000..9c1d69f --- /dev/null +++ b/mina-core/src/test/java/org/apache/mina/core/service/AbstractIoServiceDIRMINA1076Test.java @@ -0,0 +1,172 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.mina.core.service; + +import static org.junit.Assert.fail; + +import org.apache.mina.core.future.CloseFuture; +import org.apache.mina.core.future.ConnectFuture; +import org.apache.mina.core.session.IdleStatus; +import org.apache.mina.core.session.IoSession; +import org.apache.mina.filter.codec.ProtocolCodecFilter; +import org.apache.mina.filter.codec.textline.TextLineCodecFactory; +import org.apache.mina.transport.socket.nio.NioSocketAcceptor; +import org.apache.mina.transport.socket.nio.NioSocketConnector; +import org.apache.mina.util.AvailablePortFinder; +import org.junit.Test; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.charset.Charset; +import java.util.concurrent.CountDownLatch; + +/** + * Test disposal of AbstractIoService. This test should not hang or timeout when DIRMINA-1076 is fixed. + * + * @author chrjohn + */ +public class AbstractIoServiceDIRMINA1076Test { + + @Test( timeout = 15000 ) + public void testDispose() + throws Exception { + + long startTime = System.currentTimeMillis(); + // without DIRMINA-1076 fixed, the test will hang after short time + while ( System.currentTimeMillis() < startTime + 10000 ) { + Thread thread = new Thread() { + + public void run() { + + final IoAcceptor acceptor = new NioSocketAcceptor(); + acceptor.getFilterChain() + .addLast( "codec", + new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName( "UTF-8" ) ) ) ); + + acceptor.setHandler( new ServerHandler() ); + + acceptor.getSessionConfig().setReadBufferSize( 2048 ); + acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10 ); + int nextAvailable = AvailablePortFinder.getNextAvailable(); + try { + acceptor.bind( new InetSocketAddress( nextAvailable ) ); + } catch ( IOException e1 ) { + // ignore + } + + final NioSocketConnector connector = new NioSocketConnector(); + + // Set connect timeout. + connector.setConnectTimeoutMillis( 30 * 1000L ); + + connector.setHandler( new ClientHandler() ); + connector.getFilterChain() + .addLast( "codec", + new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName( "UTF-8" ) ) ) ); + + // Start communication. + ConnectFuture cf = connector.connect( new InetSocketAddress( "localhost", nextAvailable ) ); + cf.awaitUninterruptibly(); + + IoSession session = cf.getSession(); + + // send a message + session.write( "Hello World!\r" ); + + // wait until response is received + CountDownLatch latch = (CountDownLatch)session.getAttribute( "latch" ); + try { + latch.await(); + } catch ( InterruptedException e1 ) { + Thread.currentThread().interrupt(); + } + + // close the session + CloseFuture closeFuture = session.closeOnFlush(); + + connector.dispose( true ); + + closeFuture.awaitUninterruptibly(); + acceptor.dispose( true ); + } + }; + thread.setDaemon( true ); + thread.start(); + thread.join( 1000 ); + + if ( thread.isAlive() ) { + for ( StackTraceElement stackTraceElement : thread.getStackTrace() ) { + if ( "dispose".equals( stackTraceElement.getMethodName() ) + && AbstractIoService.class.getCanonicalName().equals( stackTraceElement.getClassName() ) ) { + System.err.println( "Detected hang in AbstractIoService.dispose()!" ); + } + } + fail( "Thread should have died by now, supposed hang in AbstractIoService.dispose()" ); + } + } + ; + } + + public static class ClientHandler + extends + IoHandlerAdapter { + + @Override + public void sessionCreated( IoSession session ) + throws Exception { + session.setAttribute( "latch", new CountDownLatch( 1 ) ); + } + + + + @Override + public void messageReceived( IoSession session, Object message ) + throws Exception { + CountDownLatch latch = (CountDownLatch)session.getAttribute( "latch" ); + latch.countDown(); + } + + + + @Override + public void exceptionCaught( IoSession session, Throwable cause ) + throws Exception {} + } + + public static class ServerHandler + extends + IoHandlerAdapter { + + @Override + public void messageReceived( IoSession session, Object message ) + throws Exception { + session.write( message.toString() ); + } + + + + @Override + public void exceptionCaught( IoSession session, Throwable cause ) + throws Exception {} + + } + +} http://git-wip-us.apache.org/repos/asf/mina/blob/4f9b2c60/mina-core/src/test/java/org/apache/mina/core/service/SslTestHandshakeExceptionDIRMINA1077Test.java ---------------------------------------------------------------------- diff --git a/mina-core/src/test/java/org/apache/mina/core/service/SslTestHandshakeExceptionDIRMINA1077Test.java b/mina-core/src/test/java/org/apache/mina/core/service/SslTestHandshakeExceptionDIRMINA1077Test.java new file mode 100644 index 0000000..88c3b23 --- /dev/null +++ b/mina-core/src/test/java/org/apache/mina/core/service/SslTestHandshakeExceptionDIRMINA1077Test.java @@ -0,0 +1,186 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.mina.filter.ssl; + +import static org.junit.Assert.fail; + +import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder; +import org.apache.mina.core.future.ConnectFuture; +import org.apache.mina.core.service.AbstractIoService; +import org.apache.mina.core.service.IoHandlerAdapter; +import org.apache.mina.core.session.IoSession; +import org.apache.mina.filter.codec.ProtocolCodecFilter; +import org.apache.mina.filter.codec.textline.TextLineCodecFactory; +import org.apache.mina.transport.socket.nio.NioSocketAcceptor; +import org.apache.mina.transport.socket.nio.NioSocketConnector; +import org.apache.mina.util.AvailablePortFinder; +import org.junit.Test; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManagerFactory; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.Security; + +/** + * Test a SSL session and provoke HandshakeException. + * This test should not hang or timeout when DIRMINA-1076/1077 is fixed. + * + * @author chrjohn + */ +public class SslTestHandshakeExceptionDIRMINA1077Test { + private int port = AvailablePortFinder.getNextAvailable(); + private static InetAddress address; + private static NioSocketAcceptor acceptor; + + /** A JVM independant KEY_MANAGER_FACTORY algorithm */ + private static final String KEY_MANAGER_FACTORY_ALGORITHM; + + static { + String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm"); + if (algorithm == null) { + algorithm = KeyManagerFactory.getDefaultAlgorithm(); + } + + KEY_MANAGER_FACTORY_ALGORITHM = algorithm; + } + + + private static class TestHandler extends IoHandlerAdapter { + public void messageReceived(IoSession session, Object message) throws Exception {} + + @Override + public void exceptionCaught( IoSession session, Throwable cause ) + throws Exception {} + } + + /** + * Starts a Server with the SSL Filter and a simple text line + * protocol codec filter + */ + private void startServer() throws Exception { + acceptor = new NioSocketAcceptor(); + + acceptor.setReuseAddress(true); + DefaultIoFilterChainBuilder filters = acceptor.getFilterChain(); + + // Inject the SSL filter + SslFilter sslFilter = new SslFilter(createSSLContext(true)); + filters.addLast("sslFilter", sslFilter); + sslFilter.setNeedClientAuth(true); + + // Inject the TestLine codec filter + filters.addLast("text", new ProtocolCodecFilter(new TextLineCodecFactory())); + + acceptor.setHandler(new TestHandler()); + acceptor.bind(new InetSocketAddress(port)); + } + + private static void stopServer() { + acceptor.dispose(true); + } + + private void startAndStopClient() throws Exception { + NioSocketConnector nioSocketConnector = new NioSocketConnector(); + nioSocketConnector.setHandler(new TestHandler()); + DefaultIoFilterChainBuilder filters = nioSocketConnector.getFilterChain(); + + // Inject the SSL filter + SslFilter sslFilter = new SslFilter(createSSLContext(false)); + sslFilter.setUseClientMode( true ); + filters.addLast("sslFilter", sslFilter); + + address = InetAddress.getByName("localhost"); + SocketAddress remoteAddress = new InetSocketAddress( address, port ); + ConnectFuture connect = nioSocketConnector.connect( remoteAddress ); + connect.awaitUninterruptibly(); +// System.out.println( "Closing connection..." ); + nioSocketConnector.dispose( true ); +// System.out.println( "Connection closed!" ); + } + + private static SSLContext createSSLContext(boolean emptyKeystore) throws IOException, GeneralSecurityException { + char[] passphrase = "password".toCharArray(); + + SSLContext ctx = SSLContext.getInstance("TLSv1.2"); + KeyManagerFactory kmf = KeyManagerFactory.getInstance(KEY_MANAGER_FACTORY_ALGORITHM); + TrustManagerFactory tmf = TrustManagerFactory.getInstance(KEY_MANAGER_FACTORY_ALGORITHM); + + KeyStore ks = KeyStore.getInstance("JKS"); + KeyStore ts = KeyStore.getInstance("JKS"); + + // use empty keystore to provoke handshake exception + if (emptyKeystore) { + ks.load(SslTestHandshakeExceptionDIRMINA1077Test.class.getResourceAsStream("emptykeystore.sslTest"), passphrase); + } else { + ks.load(SslTestHandshakeExceptionDIRMINA1077Test.class.getResourceAsStream("keystore.sslTest"), passphrase); + } + ts.load(SslTestHandshakeExceptionDIRMINA1077Test.class.getResourceAsStream("truststore.sslTest"), passphrase); + + kmf.init(ks, passphrase); + tmf.init(ts); + + ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + + return ctx; + } + + @Test(timeout=15000) + public void testSSL() throws Exception { + long startTime = System.currentTimeMillis(); + // without DIRMINA-1076/1077 fixed, the test will hang after short time + while (System.currentTimeMillis() < startTime + 10000) { + try { + startServer(); + + Thread t = new Thread() { + public void run() { + try { + startAndStopClient(); + } catch ( Exception e ) {} + } + }; + t.setDaemon( true ); + t.start(); + t.join( 1000 ); + + if ( t.isAlive() ) { + for ( StackTraceElement stackTraceElement : t.getStackTrace() ) { + if ( "dispose".equals( stackTraceElement.getMethodName() ) + && AbstractIoService.class.getCanonicalName() + .equals( stackTraceElement.getClassName() ) ) { + System.err.println( "Detected hang in AbstractIoService.dispose()!" ); + } + } + fail( "Thread should have died by now, supposed hang in AbstractIoService.dispose()" ); + } + } finally { + stopServer(); + } + } + } +} http://git-wip-us.apache.org/repos/asf/mina/blob/4f9b2c60/mina-core/src/test/resources/org/apache/mina/filter/ssl/emptykeystore.sslTest ---------------------------------------------------------------------- diff --git a/mina-core/src/test/resources/org/apache/mina/filter/ssl/emptykeystore.sslTest b/mina-core/src/test/resources/org/apache/mina/filter/ssl/emptykeystore.sslTest new file mode 100644 index 0000000..65d4b65 Binary files /dev/null and b/mina-core/src/test/resources/org/apache/mina/filter/ssl/emptykeystore.sslTest differ