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 }