001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 *
019 */
020package org.apache.mina.example.echoserver;
021import static org.junit.Assert.assertEquals;
022
023import java.io.IOException;
024import java.net.InetSocketAddress;
025import java.net.SocketAddress;
026import java.security.GeneralSecurityException;
027
028import org.apache.mina.core.buffer.IoBuffer;
029import org.apache.mina.core.service.IoAcceptor;
030import org.apache.mina.core.session.IoSession;
031import org.apache.mina.example.echoserver.ssl.BogusSslContextFactory;
032import org.apache.mina.filter.ssl.SslFilter;
033import org.apache.mina.transport.socket.DatagramSessionConfig;
034import org.apache.mina.transport.socket.nio.NioDatagramAcceptor;
035import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
036import org.apache.mina.util.AvailablePortFinder;
037import org.junit.After;
038import org.junit.Before;
039import org.slf4j.Logger;
040import org.slf4j.LoggerFactory;
041
042/**
043 * Tests echo server example.
044 *
045 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
046 */
047public abstract class AbstractTest {
048    private final static Logger LOGGER = LoggerFactory.getLogger(AbstractTest.class);
049
050    protected boolean useSSL;
051
052    protected int port;
053
054    protected SocketAddress boundAddress;
055
056    protected IoAcceptor datagramAcceptor;
057
058    protected IoAcceptor socketAcceptor;
059
060    protected AbstractTest() {
061        // Do nothing
062    }
063
064    protected static void isEquals(byte[] expected, byte[] actual) {
065        assertEquals(toString(expected), toString(actual));
066    }
067
068    protected static void isEquals(IoBuffer expected, IoBuffer actual) {
069        assertEquals(toString(expected), toString(actual));
070    }
071
072    protected static String toString(byte[] buf) {
073        StringBuilder str = new StringBuilder(buf.length * 4);
074        for (byte element : buf) {
075            str.append(element);
076            str.append(' ');
077        }
078        return str.toString();
079    }
080
081    protected static String toString(IoBuffer buf) {
082        return buf.getHexDump();
083    }
084
085    @Before
086    public void setUp() throws Exception {
087        // Disable SSL by default
088        useSSL = false;
089
090        boundAddress = null;
091        datagramAcceptor = new NioDatagramAcceptor();
092        socketAcceptor = new NioSocketAcceptor();
093
094        ((DatagramSessionConfig) datagramAcceptor.getSessionConfig())
095                .setReuseAddress(true);
096        ((NioSocketAcceptor) socketAcceptor).setReuseAddress(true);
097
098        // Find an available test port and bind to it.
099        boolean socketBound = false;
100        boolean datagramBound = false;
101
102        // Let's start from port #1 to detect possible resource leak
103        // because test will fail in port 1-1023 if user run this test
104        // as a normal user.
105
106        SocketAddress address = null;
107
108        // Find the first available port above 1024
109        port = AvailablePortFinder.getNextAvailable(1024);
110
111        socketBound = false;
112        datagramBound = false;
113
114        address = new InetSocketAddress(port);
115
116        try {
117            socketAcceptor.setHandler(new EchoProtocolHandler() {
118                @Override
119                public void sessionCreated(IoSession session) {
120                    if (useSSL) {
121                        try {
122                            session.getFilterChain().addFirst(
123                                    "SSL",
124                                    new SslFilter(BogusSslContextFactory
125                                            .getInstance(true)));
126                        } catch (GeneralSecurityException e) {
127                            LOGGER.error("", e);
128                            throw new RuntimeException(e);
129                        }
130                    }
131                }
132
133                // This is for TLS re-entrance test
134                @Override
135                public void messageReceived(IoSession session, Object message)
136                        throws Exception {
137                    if (!(message instanceof IoBuffer)) {
138                        return;
139                    }
140
141                    IoBuffer buf = (IoBuffer) message;
142                    
143                    buf.mark();
144
145                    if (session.getFilterChain().contains("SSL")
146                            && buf.remaining() == 1 && buf.get() == (byte) '.') {
147                        LOGGER.info("TLS Reentrance");
148                        ((SslFilter) session.getFilterChain().get("SSL"))
149                                .startSsl(session);
150
151                        // Send a response
152                        buf.capacity(1);
153                        buf.flip();
154                        session.setAttribute(SslFilter.DISABLE_ENCRYPTION_ONCE);
155                        session.write(buf);
156                    } else {
157                        buf.reset();
158                        super.messageReceived(session, buf);
159                    }
160                }
161            });
162
163            socketAcceptor.bind(address);
164            socketBound = true;
165
166            datagramAcceptor.setHandler(new EchoProtocolHandler());
167            datagramAcceptor.bind(address);
168            datagramBound = true;
169        } catch (IOException e) {
170            // Do nothing
171        } finally {
172            if (socketBound && !datagramBound) {
173                socketAcceptor.unbind();
174            }
175            if (datagramBound && !socketBound) {
176                datagramAcceptor.unbind();
177            }
178        }
179
180        // If there is no port available, test fails.
181        if (!socketBound || !datagramBound) {
182            throw new IOException("Cannot bind any test port.");
183        }
184
185        boundAddress = address;
186        LOGGER.info("Using port " + port + " for testing.");
187    }
188
189    @After
190    public void tearDown() throws Exception {
191        if (boundAddress != null) {
192            socketAcceptor.dispose();
193            datagramAcceptor.dispose();
194        }
195    }
196}