/* * PortUnificationTest.java * * Created on Feb 2, 2010, 10:26:58 AM * * Description: . * * Copyright (C) Feb 2, 2010 reed. * * This program is free software; you can redistribute it and/or modify it under the terms * of the GNU General Public License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; * if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.texai.network.netty; //import org.jboss.netty.handler.codec.http.websocketx.WebSocketVersion; import java.io.IOException; import java.net.InetSocketAddress; import java.net.URI; import java.net.URISyntaxException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.cert.CertificateException; import java.util.Enumeration; import java.util.HashMap; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import org.apache.log4j.Logger; import org.jboss.netty.bootstrap.ClientBootstrap; import org.jboss.netty.bootstrap.ServerBootstrap; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelFuture; import org.jboss.netty.channel.ChannelPipeline; import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory; import org.jboss.netty.handler.codec.http.CookieEncoder; import org.jboss.netty.handler.codec.http.DefaultHttpRequest; import org.jboss.netty.handler.codec.http.HttpHeaders; import org.jboss.netty.handler.codec.http.HttpMethod; import org.jboss.netty.handler.codec.http.HttpRequest; import org.jboss.netty.handler.codec.http.HttpVersion; import org.jboss.netty.handler.codec.http.websocketx.CloseWebSocketFrame; import org.jboss.netty.handler.codec.http.websocketx.PingWebSocketFrame; import org.jboss.netty.handler.codec.http.websocketx.TextWebSocketFrame; import org.jboss.netty.handler.codec.http.websocketx.WebSocketClientHandshaker; import org.jboss.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory; import org.jboss.netty.handler.codec.http.websocketx.WebSocketVersion; import org.junit.After; import org.junit.AfterClass; import static org.junit.Assert.*; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.texai.network.netty.handler.AbstractHTTPRequestHandlerFactory; import org.texai.network.netty.handler.AbstractHTTPResponseHandler; import org.texai.network.netty.handler.AbstractWebSocketResponseHandler; import org.texai.network.netty.handler.MockHTTPRequestHandlerFactory; import org.texai.network.netty.handler.MockHTTPResponseHandler; import org.texai.network.netty.handler.MockWebSocketResponseHandler; import org.texai.network.netty.pipeline.HTTPClientPipelineFactory; import org.texai.network.netty.pipeline.WebSocketClientPipelineFactory; import org.texai.util.StringUtils; import org.texai.util.TexaiException; import org.texai.x509.KeyStoreTestUtils; import org.texai.x509.X509SecurityInfo; import org.texai.x509.X509Utils; import com.unitt.framework.websocket.WebSocket; import com.unitt.framework.websocket.WebSocketConnectConfig; import com.unitt.framework.websocket.WebSocketObserver; import com.unitt.framework.websocket.simple.SimpleSocketFactory; /** * * @author reed */ public final class PortUnification2Test { /** the logger */ private static final Logger LOGGER = Logger.getLogger(PortUnification2Test.class); /** the server port */ private static final int SERVER_PORT = 8088; /** the client executor */ private final Executor clientExecutor = Executors.newCachedThreadPool(); /** sets debugging */ // static { // System.setProperty("javax.net.debug", "all"); // } public PortUnification2Test() { } @BeforeClass public static void setUpClass() throws Exception { } @AfterClass public static void tearDownClass() throws Exception { } @Before public void setUp() { } @After public void tearDown() { } /** * Test of port unification ability to recognize and process a serialized object. */ @Test public void testPortUnification() { LOGGER.info("port unification"); // configure the server channel pipeline factory final AbstractHTTPRequestHandlerFactory httpRequestHandlerFactory = new MockHTTPRequestHandlerFactory(); final X509SecurityInfo x509SecurityInfo = KeyStoreTestUtils.getServerX509SecurityInfo(); LOGGER.info("server x509SecurityInfo...\n" + x509SecurityInfo); final Executor serverExecutor = Executors.newCachedThreadPool(); final ServerBootstrap serverBootstrap = ConnectionUtils.createPortUnificationServer( SERVER_PORT, x509SecurityInfo, null, // albusHCSMessageHandlerFactory, null, // bitTorrentHandlerFactory, httpRequestHandlerFactory, serverExecutor, // bossExecutor serverExecutor); // workerExecutor LOGGER.info("testing clients"); // test clients webSocketClient(); // httpClient(); // nettyWebSocketClient(); // httpClient(); // webSocketClient(); // webSocketClient(); // nettyWebSocketClient(); // nettyWebSocketClient(); // httpClient(); // httpClient(); // nettyWebSocketClient(); // httpClient(); // shut down executor threads to exit // LOGGER.info("releasing server resources"); // serverBootstrap.releaseExternalResources(); sometimes hangs } /** Tests the HTTP request and response messages. */ @SuppressWarnings("ThrowableResultIgnored") private void httpClient() { final ClientBootstrap clientBootstrap = new ClientBootstrap(new NioClientSocketChannelFactory( Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); // configure the client pipeline final Object clientResume_lock = new Object(); final AbstractHTTPResponseHandler httpResponseHandler = new MockHTTPResponseHandler(clientResume_lock); final X509SecurityInfo x509SecurityInfo = KeyStoreTestUtils.getClientX509SecurityInfo(); final ChannelPipeline channelPipeline = HTTPClientPipelineFactory.getPipeline( httpResponseHandler, x509SecurityInfo); clientBootstrap.setPipeline(channelPipeline); // start the connection attempt ChannelFuture channelFuture = clientBootstrap.connect(new InetSocketAddress("localhost", SERVER_PORT)); // wait until the connection attempt succeeds or fails final Channel channel = channelFuture.awaitUninterruptibly().getChannel(); if (!channelFuture.isSuccess()) { LOGGER.info(StringUtils.getStackTraceAsString(channelFuture.getCause())); fail(channelFuture.getCause().getMessage()); } LOGGER.info("HTTP client connected"); // send the HTTP request URI uri = null; try { uri = new URI("https://localhost:" + SERVER_PORT + "/data/test.txt"); } catch (URISyntaxException ex) { fail(ex.getMessage()); } @SuppressWarnings("null") final String host = uri.getHost() == null ? "localhost" : uri.getHost(); final HttpRequest httpRequest = new DefaultHttpRequest( HttpVersion.HTTP_1_1, HttpMethod.GET, uri.toASCIIString()); httpRequest.setHeader(HttpHeaders.Names.HOST, host); httpRequest.setHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE); final CookieEncoder httpCookieEncoder = new CookieEncoder(false); httpCookieEncoder.addCookie("my-cookie", "foo"); httpCookieEncoder.addCookie("another-cookie", "bar"); httpRequest.setHeader(HttpHeaders.Names.COOKIE, httpCookieEncoder.encode()); LOGGER.info("httpRequest ...\n" + httpRequest); channel.write(httpRequest); // wait for the request message to be sent channelFuture.awaitUninterruptibly(); if (!channelFuture.isSuccess()) { LOGGER.info(StringUtils.getStackTraceAsString(channelFuture.getCause())); fail(channelFuture.getCause().getMessage()); } // the message response handler will signal this thread when the test exchanges are completed synchronized (clientResume_lock) { try { clientResume_lock.wait(); } catch (InterruptedException ex) { } } LOGGER.info("releasing HTTP client resources"); channel.close(); clientBootstrap.releaseExternalResources(); } /** Tests the Netty web socket request and response messages. */ @SuppressWarnings("ThrowableResultIgnored") private void nettyWebSocketClient() { final ClientBootstrap clientBootstrap = new ClientBootstrap(new NioClientSocketChannelFactory( Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); final Object clientResume_lock = new Object(); URI uri = null; try { // wss URI scheme indicates web socket secure connection uri = new URI("wss://localhost:" + SERVER_PORT + "/data/test.txt"); } catch (URISyntaxException ex) { fail(ex.getMessage()); } @SuppressWarnings("null") final String protocol = uri.getScheme(); if (!protocol.equals("wss")) { throw new TexaiException("Unsupported protocol: " + protocol); } // configure the client pipeline final WebSocketClientHandshaker webSocketClientHandshaker = new WebSocketClientHandshakerFactory().newHandshaker( uri, // webSocketURL WebSocketVersion.V08, // version null, // subprotocol false, // allowExtensions new HashMap<>()); // customHeaders final AbstractWebSocketResponseHandler webSocketResponseHandler = new MockWebSocketResponseHandler( webSocketClientHandshaker, clientResume_lock); final X509SecurityInfo x509SecurityInfo = KeyStoreTestUtils.getClientX509SecurityInfo(); LOGGER.info("Netty websocket client x509SecurityInfo...\n" + x509SecurityInfo); final ChannelPipeline channelPipeline = WebSocketClientPipelineFactory.getPipeline( webSocketResponseHandler, x509SecurityInfo); clientBootstrap.setPipeline(channelPipeline); // start the connection attempt ChannelFuture channelFuture = clientBootstrap.connect(new InetSocketAddress(uri.getHost(), uri.getPort())); // wait until the connection attempt succeeds or fails final Channel channel = channelFuture.awaitUninterruptibly().getChannel(); if (!channelFuture.isSuccess()) { LOGGER.info(StringUtils.getStackTraceAsString(channelFuture.getCause())); fail(channelFuture.getCause().getMessage()); } LOGGER.info("web socket client connected"); // start the handshake that upgrades HTTP to web socket protocol webSocketClientHandshaker.handshake(channel); // the message response handler will signal this thread when the web socket handshake is completed synchronized (clientResume_lock) { try { clientResume_lock.wait(); } catch (InterruptedException ex) { } } LOGGER.info("web socket client handshake completed"); LOGGER.info("client pipeline ...\n" + channel.getPipeline()); // Send 10 messages and wait for responses LOGGER.info("web socket client sending text frame messages ..."); for (int i = 0; i < 10; i++) { final String message = "Message #" + i; LOGGER.info(" " + message); channel.write(new TextWebSocketFrame(message)); } // Ping LOGGER.info("web socket client sending ping"); channel.write(new PingWebSocketFrame(ChannelBuffers.copiedBuffer(new byte[]{1, 2, 3, 4, 5, 6}))); try { // wait three seconds for server to process Thread.sleep(3_000); } catch (InterruptedException ex) { } // Close LOGGER.info("web socket client sending close"); channelFuture = channel.write(new CloseWebSocketFrame()); channelFuture.awaitUninterruptibly(10_000); // wait at most 10 seconds // WebSocketClientHandler will close the connection when the server // responds to the CloseWebSocketFrame. LOGGER.info("waiting for the web socket connection to close"); channel.getCloseFuture().awaitUninterruptibly(); LOGGER.info("releasing web socket client resources"); channel.close(); clientBootstrap.releaseExternalResources(); } /** Tests the web socket request and response messages. */ private void webSocketClient() { // configure websocket client LOGGER.info("configuring websocket client"); // production and test relative file location System.setProperty("javax.net.ssl.trustStore", "data/truststore.jks"); System.setProperty("javax.net.ssl.trustStorePassword", new String(X509Utils.TRUSTSTORE_PASSWORD)); try { final KeyStore truststore = X509Utils.findOrCreateJKSKeyStore("data/truststore.jks", X509Utils.TRUSTSTORE_PASSWORD); final Enumeration<String> aliases = truststore.aliases(); int aliasCnt = 0; while (aliases.hasMoreElements()) { LOGGER.info("alias: " + aliases.nextElement()); aliasCnt++; } LOGGER.info("******************** assert fails"); assertTrue(aliasCnt > 0); } catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException | NoSuchProviderException ex) { fail(ex.getMessage()); } URI uri = null; try { // wss URI scheme indicates web socket secure connection uri = new URI("wss://localhost:" + SERVER_PORT + "/data/any.txt"); } catch (URISyntaxException ex) { fail(ex.getMessage()); } @SuppressWarnings("null") final String protocol = uri.getScheme(); if (!protocol.equals("wss")) { throw new TexaiException("Unsupported protocol: " + protocol); } final WebSocket webSocket; final WebSocketConnectConfig config = new WebSocketConnectConfig(); config.setUrl(uri); config.setWebSocketVersion(com.unitt.framework.websocket.WebSocketConnectConfig.WebSocketVersion.Version08); final WebSocketObserver mockWebSocketObserver = new MockWebSocketObserver(); webSocket = SimpleSocketFactory.create(config, mockWebSocketObserver); LOGGER.info("opening client web socket"); webSocket.open(); // Send 10 messages and wait for responses LOGGER.info("web socket client sending text frame messages ..."); for (int i = 0; i < 10; i++) { final String message = "Message #" + i; LOGGER.info(" " + message); webSocket.sendMessage(message); } // Ping LOGGER.info("web socket client sending ping"); webSocket.ping("my ping"); // wait three seconds for the messages to process try { Thread.sleep(3_000); } catch (InterruptedException ex) { } } }