    1   /*
    2    *  Licensed to the Apache Software Foundation (ASF) under one
    3    *  or more contributor license agreements.  See the NOTICE file
    4    *  distributed with this work for additional information
    5    *  regarding copyright ownership.  The ASF licenses this file
    6    *  to you under the Apache License, Version 2.0 (the
    7    *  "License"); you may not use this file except in compliance
    8    *  with the License.  You may obtain a copy of the License at
    9    *
    10   *    http://www.apache.org/licenses/LICENSE-2.0
    11   *
    12   *  Unless required by applicable law or agreed to in writing,
    13   *  software distributed under the License is distributed on an
    14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    15   *  KIND, either express or implied.  See the License for the
    16   *  specific language governing permissions and limitations
    17   *  under the License.
    18   *
    19   */
    20  package org.apache.mina.example.echoserver;
    21
    22  import java.net.InetSocketAddress;
    23
    24  import junit.framework.Assert;
    25
    26  import org.apache.mina.common.ByteBuffer;
    27  import org.apache.mina.common.ConnectFuture;
    28  import org.apache.mina.common.IoConnector;
    29  import org.apache.mina.common.IoHandlerAdapter;
    30  import org.apache.mina.common.IoSession;
    31  import org.apache.mina.common.RuntimeIOException;
    32  import org.apache.mina.common.WriteFuture;
    33  import org.apache.mina.example.echoserver.ssl.BogusSSLContextFactory;
    34  import org.apache.mina.filter.SSLFilter;
    35  import org.apache.mina.transport.socket.nio.DatagramConnector;
    36  import org.apache.mina.transport.socket.nio.SocketConnector;
    37  import org.apache.mina.util.AvailablePortFinder;
    38  import org.apache.mina.util.SessionLog;
    39
    40  /**
    41   * Tests echo server example.
    42   *
    43   * @author The Apache Directory Project (mina-dev@directory.apache.org)
    44   * @version $Rev:448075 $, $Date:2006-09-20 05:26:53Z $
    45   */
    46  public class ConnectorTest extends AbstractTest {
    47      private static final int TIMEOUT = 10000; // 10 seconds
    48
    49      private final int COUNT = 10;
    50
    51      private final int DATA_SIZE = 16;
    52
    53      private SSLFilter connectorSSLFilter;
    54
    55      public ConnectorTest() {
    56      }
    57
    58      protected void setUp() throws Exception {
    59          super.setUp();
    60
    61          connectorSSLFilter = new SSLFilter(BogusSSLContextFactory
    62                  .getInstance(false));
    63          connectorSSLFilter.setUseClientMode(true); // set client mode
    64      }
    65
    66      public void testTCP() throws Exception {
    67          IoConnector connector = new SocketConnector();
    68          testConnector(connector);
    69      }
    70
    71      public void testTCPWithSSL() throws Exception {
    72          useSSL = true;
    73          // Create a connector
    74          IoConnector connector = new SocketConnector();
    75
    76          // Add an SSL filter to connector
    77          connector.getDefaultConfig().getFilterChain().addLast("SSL",
    78                  connectorSSLFilter);
    79          testConnector(connector);
    80      }
    81
    82      public void testUDP() throws Exception {
    83          IoConnector connector = new DatagramConnector();
    84          testConnector(connector);
    85      }
    86
    87      private void testConnector(IoConnector connector) throws Exception {
    88          System.out.println("* Without localAddress");
    89          testConnector(connector, false);
    90
    91          System.out.println("* With localAddress");
    92          testConnector(connector, true);
    93      }
    94
    95      private void testConnector(IoConnector connector, boolean useLocalAddress)
    96              throws Exception {
    97          EchoConnectorHandler handler = new EchoConnectorHandler();
    98
    99          IoSession session = null;
    100         if (!useLocalAddress) {
    101             ConnectFuture future = connector.connect(new InetSocketAddress(
    102                     "localhost", port), handler);
    103             future.join();
    104             session = future.getSession();
    105         } else {
    106             int clientPort = port;
    107             for (int i = 0; i < 65536; i++) {
    108                 clientPort = AvailablePortFinder
    109                         .getNextAvailable(clientPort + 1);
    110                 try {
    111                     ConnectFuture future = connector.connect(
    112                             new InetSocketAddress("localhost", port),
    113                             new InetSocketAddress(clientPort), handler);
    114                     future.join();
    115                     session = future.getSession();
    116                     break;
    117                 } catch (RuntimeIOException e) {
    118                     // Try again until we succeed to bind.
    119                 }
    120             }
    121
    122             if (session == null) {
    123                 Assert.fail("Failed to find out an appropriate local address.");
    124             }
    125         }
    126
    127         // Run a basic connector test.
    128         testConnector0(session);
    129
    130         // Send closeNotify to test TLS closure if it is TLS connection.
    131         if (useSSL) {
    132             connectorSSLFilter.stopSSL(session).join();
    133
    134             System.out
    135                     .println("-------------------------------------------------------------------------------");
    136             // Test again after we finished TLS session.
    137             testConnector0(session);
    138
    139             System.out
    140                     .println("-------------------------------------------------------------------------------");
    141
    142             // Test if we can enter TLS mode again.
    143             //// Send StartTLS request.
    144             handler.readBuf.clear();
    145             ByteBuffer buf = ByteBuffer.allocate(1);
    146             buf.put((byte) '.');
    147             buf.flip();
    148             session.write(buf).join();
    149
    150             //// Wait for StartTLS response.
    151             waitForResponse(handler, 1);
    152
    153             handler.readBuf.flip();
    154             Assert.assertEquals(1, handler.readBuf.remaining());
    155             Assert.assertEquals((byte) '.', handler.readBuf.get());
    156
    157             // Now start TLS connection
    158             Assert.assertTrue(connectorSSLFilter.startSSL(session));
    159             testConnector0(session);
    160         }
    161
    162         session.close().join();
    163     }
    164
    165     private void testConnector0(IoSession session) throws InterruptedException {
    166         EchoConnectorHandler handler = (EchoConnectorHandler) session
    167                 .getHandler();
    168         ByteBuffer readBuf = handler.readBuf;
    169         readBuf.clear();
    170         WriteFuture writeFuture = null;
    171         for (int i = 0; i < COUNT; i++) {
    172             ByteBuffer buf = ByteBuffer.allocate(DATA_SIZE);
    173             buf.limit(DATA_SIZE);
    174             fillWriteBuffer(buf, i);
    175             buf.flip();
    176
    177             writeFuture = session.write(buf);
    178
    179             if (session.getTransportType().isConnectionless()) {
    180                 // This will align message arrival order in connectionless transport types
    181                 waitForResponse(handler, (i + 1) * DATA_SIZE);
    182             }
    183         }
    184
    185         writeFuture.join();
    186
    187         waitForResponse(handler, DATA_SIZE * COUNT);
    188
    189         // Assert data
    190         //// Please note that BufferOverflowException can be thrown
    191         //// in SocketIoProcessor if there was a read timeout because
    192         //// we share readBuf.
    193         readBuf.flip();
    194         SessionLog.info(session, "readBuf: " + readBuf);
    195         Assert.assertEquals(DATA_SIZE * COUNT, readBuf.remaining());
    196         ByteBuffer expectedBuf = ByteBuffer.allocate(DATA_SIZE * COUNT);
    197         for (int i = 0; i < COUNT; i++) {
    198             expectedBuf.limit((i + 1) * DATA_SIZE);
    199             fillWriteBuffer(expectedBuf, i);
    200         }
    201         expectedBuf.position(0);
    202
    203         assertEquals(expectedBuf, readBuf);
    204     }
    205
    206     private void waitForResponse(EchoConnectorHandler handler, int bytes)
    207             throws InterruptedException {
    208         for (int j = 0; j < TIMEOUT / 10; j++) {
    209             if (handler.readBuf.position() >= bytes) {
    210                 break;
    211             }
    212             Thread.sleep(10);
    213         }
    214
    215         Assert.assertEquals(bytes, handler.readBuf.position());
    216     }
    217
    218     private void fillWriteBuffer(ByteBuffer writeBuf, int i) {
    219         while (writeBuf.remaining() > 0) {
    220             writeBuf.put((byte) (i++));
    221         }
    222     }
    223
    224     public static void main(String[] args) {
    225         junit.textui.TestRunner.run(ConnectorTest.class);
    226     }
    227
    228     private static class EchoConnectorHandler extends IoHandlerAdapter {
    229         private ByteBuffer readBuf = ByteBuffer.allocate(1024);
    230
    231         private EchoConnectorHandler() {
    232             readBuf.setAutoExpand(true);
    233         }
    234
    235         public void messageReceived(IoSession session, Object message) {
    236             readBuf.put((ByteBuffer) message);
    237         }
    238
    239         public void messageSent(IoSession session, Object message) {
    240         }
    241
    242         public void exceptionCaught(IoSession session, Throwable cause) {
    243             cause.printStackTrace();
    244         }
    245     }
    246 }