package com.limegroup.gnutella.rudp;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.channels.SelectableChannel;
import junit.framework.Test;
import org.limewire.core.settings.ConnectionSettings;
import org.limewire.core.settings.FilterSettings;
import org.limewire.core.settings.NetworkSettings;
import org.limewire.gnutella.tests.LimeTestCase;
import org.limewire.gnutella.tests.LimeTestUtils;
import org.limewire.listener.EventListenerList;
import org.limewire.nio.NIODispatcher;
import org.limewire.nio.observer.IOErrorObserver;
import org.limewire.rudp.UDPConnectionProcessor;
import org.limewire.rudp.UDPSelectorProvider;
import org.limewire.rudp.UDPSocketChannel;
import org.limewire.rudp.UDPSocketChannelConnectionEvent;
import org.limewire.rudp.messages.RUDPMessage;
import org.limewire.rudp.messages.SynMessage.Role;
import org.limewire.util.PrivilegedAccessor;
import com.google.inject.Injector;
import com.limegroup.gnutella.LifecycleManager;
import com.limegroup.gnutella.MessageRouter;
import com.limegroup.gnutella.NetworkManager;
import com.limegroup.gnutella.ReplyHandler;
import com.limegroup.gnutella.UDPService;
import com.limegroup.gnutella.messagehandlers.MessageHandler;
import com.limegroup.gnutella.messages.Message;
import com.limegroup.gnutella.rudp.messages.LimeRUDPMessageHelper;
import com.limegroup.gnutella.rudp.messages.RUDPMessageHandlerHelper;
public class RUDPIntegrationTest extends LimeTestCase {
private static final int PORT = 9313;
private LifecycleManager lifecycleManager;
private UDPSelectorProvider selectorProvider;
private NetworkManager networkManager;
private UDPService udpService;
private MessageRouter messageRouter;
public RUDPIntegrationTest(String name) {
super(name);
}
public static Test suite() {
return buildTestSuite(RUDPIntegrationTest.class);
}
public static void main(String[] args) {
junit.textui.TestRunner.run(suite());
}
private static void setSettings() throws Exception {
String localIP = InetAddress.getLocalHost().getHostAddress();
FilterSettings.BLACK_LISTED_IP_ADDRESSES.set(new String[] {"*.*.*.*"});
FilterSettings.WHITE_LISTED_IP_ADDRESSES.set(new String[] {"127.*.*.*", localIP});
NetworkSettings.PORT.setValue(PORT);
ConnectionSettings.CONNECT_ON_STARTUP.setValue(false);
ConnectionSettings.LOCAL_IS_PRIVATE.setValue(false);
ConnectionSettings.WATCHDOG_ACTIVE.setValue(false);
}
@Override
public void setUp() throws Exception {
setSettings();
Injector injector = LimeTestUtils.createInjector();
lifecycleManager = injector.getInstance(LifecycleManager.class);
selectorProvider = injector.getInstance(UDPSelectorProvider.class);
networkManager = injector.getInstance(NetworkManager.class);
udpService = injector.getInstance(UDPService.class);
messageRouter = injector.getInstance(MessageRouter.class);
lifecycleManager.start();
}
@Override
protected void tearDown() throws Exception {
lifecycleManager.shutdown();
}
@SuppressWarnings("unchecked")
public void testLimeRUDPMessageHandlerIsInstalled() throws Exception {
Class[] handledMessageClasses = RUDPMessageHandlerHelper.getMessageClasses();
for (Class clazz : handledMessageClasses) {
assertEquals("LimeRUDPMessageHandler", messageRouter.getUDPMessageHandler(clazz).getClass().getSimpleName());
}
}
public void testIncomingUDPPacketsSentToHandler() throws Exception {
StubRUDPMessageHandler handler = new StubRUDPMessageHandler();
RUDPMessageHandlerHelper.addHandler(messageRouter, handler);
checkMessage(handler, LimeRUDPMessageHelper.getAck(1));
checkMessage(handler, LimeRUDPMessageHelper.getData(1));
checkMessage(handler, LimeRUDPMessageHelper.getFin(1));
checkMessage(handler, LimeRUDPMessageHelper.getKeepAlive(1));
checkMessage(handler, LimeRUDPMessageHelper.getSyn(1));
}
public void testPacketsSentToMultiplexorAndGoesToProcessor() throws Exception {
StubUDPConnectionProcessor stub = new StubUDPConnectionProcessor(selectorProvider);
SelectableChannel channel = createUDPSocketChannel(stub);
assertEquals(0, stub.id);
// This is done only to allow the multiplexor to learn about the processor.
NIODispatcher.instance().register(channel, new StubIOErrorObserver());
Thread.sleep(100);
assertNotEquals(0, stub.id);
InetSocketAddress addr = new InetSocketAddress("127.0.0.1", networkManager.getNonForcedPort());
stub.addr = addr;
stub.isConnecting = false;
checkMessage(stub, LimeRUDPMessageHelper.getAck(stub.id));
checkMessage(stub, LimeRUDPMessageHelper.getData(stub.id));
checkMessage(stub, LimeRUDPMessageHelper.getFin(stub.id));
checkMessage(stub, LimeRUDPMessageHelper.getKeepAlive(stub.id));
checkMessage(stub, LimeRUDPMessageHelper.getSyn(stub.id));
channel.close();
}
private SelectableChannel createUDPSocketChannel(UDPConnectionProcessor stub) throws Exception {
Class clazz = Class.forName("org.limewire.rudp.UDPSocketChannel");
return (SelectableChannel)PrivilegedAccessor.invokeConstructor(clazz, new Object[] { stub, Role.UNDEFINED }, new Class[] { UDPConnectionProcessor.class, Role.class });
}
private void checkMessage(MessageObserver handler, Message m) throws Exception {
assertNull(handler.getCurrentMessage());
sendToUDP(m);
Message read = handler.getLastMessage(100);
assertNotNull(read);
assertEquals(m.getClass(), read.getClass());
ByteArrayOutputStream out = new ByteArrayOutputStream();
m.write(out);
byte[] b1 = out.toByteArray();
out.reset();
read.write(out);
byte[] b2 = out.toByteArray();
assertEquals(b1, b2);
}
private void sendToUDP(Message m) throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
m.write(out);
udpService.send(m, new InetSocketAddress("127.0.0.1", PORT));
}
private interface MessageObserver {
public Message getCurrentMessage();
public Message getLastMessage(int waitTime) throws Exception;
}
private class StubRUDPMessageHandler implements MessageHandler, MessageObserver {
private Message lastMessage;
public void handleMessage(Message msg, InetSocketAddress addr, ReplyHandler handler) {
synchronized(this) {
lastMessage = msg;
notify();
}
}
public synchronized Message getCurrentMessage() {
return lastMessage;
}
public Message getLastMessage(int waitTime) throws Exception {
synchronized(this) {
if(lastMessage == null)
wait(waitTime);
Message m = lastMessage;
lastMessage = null;
return m;
}
}
}
private static class StubUDPSocketChannel extends UDPSocketChannel {
StubUDPSocketChannel(UDPSelectorProvider provider) {
super(provider, provider.getContext(), Role.UNDEFINED, new EventListenerList<UDPSocketChannelConnectionEvent>());
}
}
private class StubUDPConnectionProcessor extends UDPConnectionProcessor implements MessageObserver {
private volatile byte id;
private volatile InetSocketAddress addr;
private volatile RUDPMessage lastMessage;
private volatile boolean isConnecting;
StubUDPConnectionProcessor(UDPSelectorProvider provider) {
super(new StubUDPSocketChannel(provider), provider.getContext(),
Role.UNDEFINED, new EventListenerList<UDPSocketChannelConnectionEvent>());
}
@Override
protected void setConnectionId(byte id) {
this.id = id;
super.setConnectionId(id);
}
@Override
protected InetSocketAddress getSocketAddress() {
return addr;
}
@Override
protected synchronized void handleMessage(RUDPMessage msg) {
this.lastMessage = msg;
notify();
}
@Override
protected synchronized boolean isConnecting() {
return isConnecting;
}
public synchronized Message getLastMessage(int waitTime) throws Exception {
if(lastMessage == null)
wait(waitTime);
Message m = (Message)lastMessage;
lastMessage = null;
return m;
}
public synchronized Message getCurrentMessage() {
return (Message)lastMessage;
}
}
private class StubIOErrorObserver implements IOErrorObserver {
public void handleIOException(IOException iox) {
}
public void shutdown() {
}
}
}