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