/*
* 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) {
}
}
}