/* * 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 org.jboss.netty.handler.codec.http.websocketx.PingWebSocketFrame; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.handler.codec.http.websocketx.TextWebSocketFrame; import org.texai.network.netty.handler.MockWebSocketResponseHandler; //import org.jboss.netty.handler.codec.http.websocketx.WebSocketVersion; import org.jboss.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory; import org.jboss.netty.handler.codec.http.websocketx.WebSocketClientHandshaker; import org.texai.network.netty.pipeline.WebSocketClientPipelineFactory; import org.texai.network.netty.handler.AbstractWebSocketResponseHandler; import org.jboss.netty.handler.codec.http.websocketx.CloseWebSocketFrame; import org.texai.util.TexaiException; import org.texai.util.StringUtils; import java.net.InetSocketAddress; import java.net.URI; import java.net.URISyntaxException; import java.security.cert.X509Certificate; import java.util.HashMap; import java.util.Map; import java.util.UUID; 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.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.joda.time.DateTime; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.openrdf.model.impl.URIImpl; import org.texai.ahcsSupport.Message; import org.texai.kb.Constants; import org.texai.network.netty.handler.AbstractAlbusHCSMessageHandler; import org.texai.network.netty.handler.AbstractAlbusHCSMessageHandlerFactory; import org.texai.network.netty.handler.AbstractBitTorrentHandler; import org.texai.network.netty.handler.AbstractBitTorrentHandlerFactory; import org.texai.network.netty.handler.AbstractHTTPRequestHandlerFactory; import org.texai.network.netty.handler.AbstractHTTPResponseHandler; import org.texai.network.netty.handler.MockAlbusHCSMessageHandler; import org.texai.network.netty.handler.MockAlbusHCSMessageHandlerFactory; import org.texai.network.netty.handler.MockBitTorrentHandler; import org.texai.network.netty.handler.MockBitTorrentHandlerFactory; import org.texai.network.netty.handler.MockHTTPRequestHandlerFactory; import org.texai.network.netty.handler.MockHTTPResponseHandler; import org.texai.network.netty.pipeline.BitTorrentClientPipelineFactory; import org.texai.network.netty.pipeline.HTTPClientPipelineFactory; import org.texai.torrent.message.BitTorrentHandshakeMessage; import org.texai.x509.KeyStoreTestUtils; import org.texai.x509.X509SecurityInfo; import static org.junit.Assert.*; /** * * @author reed */ public final class PortUnificationTest { /** the logger */ private static final Logger LOGGER = Logger.getLogger(PortUnificationTest.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 PortUnificationTest() { } @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 AbstractAlbusHCSMessageHandlerFactory albusHCSMessageHandlerFactory = new MockAlbusHCSMessageHandlerFactory(); final AbstractBitTorrentHandlerFactory bitTorrentHandlerFactory = new MockBitTorrentHandlerFactory(); 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, albusHCSMessageHandlerFactory, bitTorrentHandlerFactory, httpRequestHandlerFactory, serverExecutor, // bossExecutor serverExecutor); // workerExecutor LOGGER.info("testing clients"); // test clients httpClient(); nettyWebSocketClient(); albusClient(); bitTorrentClient(); httpClient(); nettyWebSocketClient(); nettyWebSocketClient(); bitTorrentClient(); albusClient(); httpClient(); httpClient(); albusClient(); albusClient(); bitTorrentClient(); bitTorrentClient(); nettyWebSocketClient(); // shut down executor threads to exit // LOGGER.info("releasing server resources"); // serverBootstrap.releaseExternalResources(); sometimes hangs } /** Tests the exchange of serialized object messages. */ @SuppressWarnings("ThrowableResultIgnored") private void albusClient() { // configure the client pipeline final Object clientResume_lock = new Object(); final AbstractAlbusHCSMessageHandler albusHCSMessageHandler = new MockAlbusHCSMessageHandler(clientResume_lock, 10); final X509SecurityInfo x509SecurityInfo = KeyStoreTestUtils.getClientX509SecurityInfo(); final Channel channel = ConnectionUtils.openAlbusHCSConnection( new InetSocketAddress("localhost", SERVER_PORT), x509SecurityInfo, albusHCSMessageHandler, clientExecutor, // bossExecutor clientExecutor); // workerExecutor LOGGER.info("Albus client connected"); // send a message final org.openrdf.model.URI senderRoleId = randomURI(); final org.openrdf.model.URI recipientRoleId = randomURI(); final UUID conversationId = UUID.randomUUID(); final UUID replyWith = UUID.randomUUID(); final UUID inReplyTo = UUID.randomUUID(); final DateTime replyByDateTime = null; final String operation = "Echo_Task"; final Map<String, Object> parameterDictionary = new HashMap<>(); Message message = new Message( senderRoleId, "TestSenderService", recipientRoleId, conversationId, replyWith, inReplyTo, replyByDateTime, "TestService", operation, parameterDictionary, "1.0.0"); // version String parameterName = "count"; Object parameterValue = 0; message.put(parameterName, parameterValue); final ChannelFuture channelFuture = channel.write(message); // 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 Albus client resources"); channel.close(); } /** 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 exchange of bit torrent messages. */ @SuppressWarnings("ThrowableResultIgnored") private void bitTorrentClient() { final ClientBootstrap clientBootstrap = new ClientBootstrap(new NioClientSocketChannelFactory( Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); // configure the client pipeline final Object clientResume_lock = new Object(); final AbstractBitTorrentHandler bitTorrentHandler = new MockBitTorrentHandler( clientResume_lock, 1); // iterations final X509SecurityInfo x509SecurityInfo = KeyStoreTestUtils.getClientX509SecurityInfo(); final ChannelPipeline channelPipeline = BitTorrentClientPipelineFactory.getPipeline( bitTorrentHandler, x509SecurityInfo); clientBootstrap.setPipeline(channelPipeline); // start the connection attempt final 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("bit torrent client connected"); // send a message final byte[] infoHash = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14}; final byte[] peerIdBytes = {0x14, 0x13, 0x12, 0x11, 0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01}; BitTorrentHandshakeMessage bitTorrentHandshakeMessage = new BitTorrentHandshakeMessage(infoHash, peerIdBytes); channel.write(bitTorrentHandshakeMessage); // 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 bit torrent client resources"); channel.close(); clientBootstrap.releaseExternalResources(); } public static org.openrdf.model.URI randomURI() { return new URIImpl(Constants.TEXAI_NAMESPACE + UUID.randomUUID().toString()); } private final Map<UUID, X509Certificate> certificateDictionary = new HashMap<>(); }