View Javadoc
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 static org.junit.Assert.assertEquals;
23  import static org.junit.Assert.assertTrue;
24  import static org.junit.Assert.fail;
25  
26  import java.net.InetSocketAddress;
27  
28  import org.apache.mina.core.buffer.IoBuffer;
29  import org.apache.mina.core.future.ConnectFuture;
30  import org.apache.mina.core.future.WriteFuture;
31  import org.apache.mina.core.service.IoConnector;
32  import org.apache.mina.core.service.IoHandlerAdapter;
33  import org.apache.mina.core.session.IoSession;
34  import org.apache.mina.core.write.WriteException;
35  import org.apache.mina.example.echoserver.ssl.BogusSslContextFactory;
36  import org.apache.mina.filter.ssl.SslFilter;
37  import org.apache.mina.transport.socket.nio.NioDatagramConnector;
38  import org.apache.mina.transport.socket.nio.NioSocketConnector;
39  import org.apache.mina.util.AvailablePortFinder;
40  import org.junit.Before;
41  import org.junit.Test;
42  import org.slf4j.Logger;
43  import org.slf4j.LoggerFactory;
44  
45  /**
46   * Tests echo server example.
47   *
48   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
49   */
50  public class ConnectorTest extends AbstractTest {
51      private final static Logger LOGGER = LoggerFactory.getLogger(ConnectorTest.class);
52  
53      private static final int TIMEOUT = 10000; // 10 seconds
54  
55      private final int COUNT = 10;
56  
57      private final int DATA_SIZE = 16;
58  
59      private EchoConnectorHandler handler;
60      private SslFilter connectorSSLFilter;
61  
62      public ConnectorTest() {
63          // Do nothing
64      }
65  
66      @Before
67      public void setUp() throws Exception {
68          super.setUp();
69          handler = new EchoConnectorHandler();
70          connectorSSLFilter = new SslFilter(BogusSslContextFactory
71                  .getInstance(false));
72          connectorSSLFilter.setUseClientMode(true); // set client mode
73      }
74  
75      @Test
76      public void testTCP() throws Exception {
77          IoConnector connector = new NioSocketConnector();
78          testConnector(connector);
79      }
80  
81      @Test
82      public void testTCPWithSSL() throws Exception {
83          useSSL = true;
84          // Create a connector
85          IoConnector connector = new NioSocketConnector();
86  
87          // Add an SSL filter to connector
88          connector.getFilterChain().addLast("SSL", connectorSSLFilter);
89          testConnector(connector);
90      }
91  
92      @Test
93      public void testUDP() throws Exception {
94          IoConnector connector = new NioDatagramConnector();
95          testConnector(connector);
96      }
97  
98      private void testConnector(IoConnector connector) throws Exception {
99          connector.setHandler(handler);
100 
101         //System.out.println("* Without localAddress");
102         testConnector(connector, false);
103 
104         //System.out.println("* With localAddress");
105         testConnector(connector, true);
106     }
107 
108     private void testConnector(IoConnector connector, boolean useLocalAddress)
109             throws Exception {
110         IoSession session = null;
111         if (!useLocalAddress) {
112             ConnectFuture future = connector.connect(new InetSocketAddress(
113                     "127.0.0.1", port));
114             future.awaitUninterruptibly();
115             session = future.getSession();
116         } else {
117             int clientPort = AvailablePortFinder.getNextAvailable();
118             ConnectFuture future = connector.connect(
119                     new InetSocketAddress("127.0.0.1", port),
120                     new InetSocketAddress(clientPort));
121             future.awaitUninterruptibly();
122             session = future.getSession();
123 
124             if (session == null) {
125                 fail("Failed to find out an appropriate local address.");
126             }
127         }
128 
129         // Run a basic connector test.
130         testConnector0(session);
131 
132         // Send closeNotify to test TLS closure if it is TLS connection.
133         if (useSSL) {
134             connectorSSLFilter.stopSsl(session).awaitUninterruptibly();
135 
136             System.out
137                     .println("-------------------------------------------------------------------------------");
138             // Test again after we finished TLS session.
139             testConnector0(session);
140 
141             System.out
142                     .println("-------------------------------------------------------------------------------");
143 
144             // Test if we can enter TLS mode again.
145             //// Send StartTLS request.
146             handler.readBuf.clear();
147             IoBuffer buf = IoBuffer.allocate(1);
148             buf.put((byte) '.');
149             buf.flip();
150             session.write(buf).awaitUninterruptibly();
151 
152             //// Wait for StartTLS response.
153             waitForResponse(handler, 1);
154 
155             handler.readBuf.flip();
156             assertEquals(1, handler.readBuf.remaining());
157             assertEquals((byte) '.', handler.readBuf.get());
158 
159             // Now start TLS connection
160             assertTrue(connectorSSLFilter.startSsl(session));
161             testConnector0(session);
162         }
163 
164         session.closeNow().awaitUninterruptibly();
165     }
166 
167     private void testConnector0(IoSession session) throws InterruptedException {
168         EchoConnectorHandler handler = (EchoConnectorHandler) session
169                 .getHandler();
170         IoBuffer readBuf = handler.readBuf;
171         readBuf.clear();
172         WriteFuture writeFuture = null;
173         
174         for (int i = 0; i < COUNT; i++) {
175             IoBuffer buf = IoBuffer.allocate(DATA_SIZE);
176             buf.limit(DATA_SIZE);
177             fillWriteBuffer(buf, i);
178             buf.flip();
179 
180             writeFuture = session.write(buf);
181 
182             if (session.getService().getTransportMetadata().isConnectionless()) {
183                 // This will align message arrival order in connectionless transport types
184                 waitForResponse(handler, (i + 1) * DATA_SIZE);
185             }
186         }
187 
188         writeFuture.awaitUninterruptibly();
189 
190         waitForResponse(handler, DATA_SIZE * COUNT);
191 
192         // Assert data
193         //// Please note that BufferOverflowException can be thrown
194         //// in SocketIoProcessor if there was a read timeout because
195         //// we share readBuf.
196         readBuf.flip();
197         LOGGER.info("readBuf: " + readBuf);
198         assertEquals(DATA_SIZE * COUNT, readBuf.remaining());
199         IoBuffer expectedBuf = IoBuffer.allocate(DATA_SIZE * COUNT);
200         
201         for (int i = 0; i < COUNT; i++) {
202             expectedBuf.limit((i + 1) * DATA_SIZE);
203             fillWriteBuffer(expectedBuf, i);
204         }
205         
206         expectedBuf.position(0);
207 
208         isEquals(expectedBuf, readBuf);
209     }
210 
211     private void waitForResponse(EchoConnectorHandler handler, int bytes)
212             throws InterruptedException {
213         for (int j = 0; j < TIMEOUT / 10; j++) {
214             if (handler.readBuf.position() >= bytes) {
215                 break;
216             }
217             Thread.sleep(10);
218         }
219 
220         assertEquals(bytes, handler.readBuf.position());
221     }
222 
223     private void fillWriteBuffer(IoBuffer writeBuf, int i) {
224         while (writeBuf.remaining() > 0) {
225             writeBuf.put((byte) i++);
226         }
227     }
228 
229     private static class EchoConnectorHandler extends IoHandlerAdapter {
230         private final IoBuffer readBuf = IoBuffer.allocate(1024);
231 
232         private EchoConnectorHandler() {
233             readBuf.setAutoExpand(true);
234         }
235 
236         @Override
237         public void messageReceived(IoSession session, Object message) {
238             readBuf.put((IoBuffer) message);
239         }
240 
241         @Override
242         public void messageSent(IoSession session, Object message) {
243         }
244 
245         @Override
246         public void exceptionCaught(IoSession session, Throwable cause) {
247             LOGGER.warn("Unexpected exception.", cause);
248             if (cause instanceof WriteException) {
249                 WriteException e = (WriteException) cause;
250                 LOGGER.warn("Failed write requests: {}", e.getRequests());
251             }
252         }
253     }
254 }