package com.limegroup.gnutella.dht;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketTimeoutException;
import java.util.Arrays;
import java.util.Set;
import junit.framework.Test;
import org.limewire.collection.FixedSizeLIFOSet;
import org.limewire.collection.FixedSizeLIFOSet.EjectionPolicy;
import org.limewire.core.settings.ConnectionSettings;
import org.limewire.core.settings.DHTSettings;
import org.limewire.io.IpPort;
import org.limewire.io.IpPortImpl;
import org.limewire.mojito.MojitoDHT;
import org.limewire.util.PrivilegedAccessor;
import com.google.inject.AbstractModule;
import com.google.inject.Injector;
import com.google.inject.Stage;
import com.limegroup.gnutella.ConnectionManager;
import com.limegroup.gnutella.ExtendedEndpoint;
import com.limegroup.gnutella.HostCatcher;
import com.limegroup.gnutella.LifecycleManager;
import com.limegroup.gnutella.LimeTestUtils;
import com.limegroup.gnutella.NetworkManager;
import com.limegroup.gnutella.dht.DHTManager.DHTMode;
import com.limegroup.gnutella.messages.Message;
import com.limegroup.gnutella.messages.MessageFactory;
import com.limegroup.gnutella.messages.PingReply;
import com.limegroup.gnutella.messages.PingReplyFactory;
import com.limegroup.gnutella.messages.PingRequest;
import com.limegroup.gnutella.messages.Message.Network;
import com.limegroup.gnutella.stubs.ConnectionManagerStub;
public class DHTNodeFetcherTest extends DHTTestCase {
/**
* Ultrapeer 1 UDP connection.
*/
private DatagramSocket[] UDP_ACCESS;
private DHTBootstrapperStub dhtBootstrapper;
private Injector injector;
private DHTNodeFetcherFactory dhtNodeFetcherFactory;
private MessageFactory messageFactory;
private MojitoDHT bootstrapDHT;
public DHTNodeFetcherTest(String name) {
super(name);
}
public static Test suite() {
return buildTestSuite(DHTNodeFetcherTest.class);
}
public static void main(String[] args) {
junit.textui.TestRunner.run(suite());
}
@Override
protected void setUp() throws Exception {
DHTTestUtils.setSettings(PORT);
ConnectionSettings.FILTER_CLASS_C.setValue(false);
dhtBootstrapper = new DHTBootstrapperStub();
UDP_ACCESS = new DatagramSocket[10];
for (int i = 0; i < UDP_ACCESS.length; i++) {
UDP_ACCESS[i] = new DatagramSocket();
UDP_ACCESS[i].setSoTimeout(10000);
}
// fake a connection to the network
injector = LimeTestUtils.createInjector(Stage.PRODUCTION, new AbstractModule() {
@Override
protected void configure() {
bind(ConnectionManager.class).to(ConnectionManagerStub.class);
}
});
ConnectionManagerStub connectionManager = (ConnectionManagerStub) injector.getInstance(ConnectionManager.class);
connectionManager.setConnected(true);
dhtNodeFetcherFactory = injector.getInstance(DHTNodeFetcherFactory.class);
messageFactory = injector.getInstance(MessageFactory.class);
bootstrapDHT = startBootstrapDHT(injector.getInstance(LifecycleManager.class));
}
@Override
protected void tearDown() throws Exception {
bootstrapDHT.close();
LimeTestUtils.waitForNIO();
}
public void testRequestDHTHostsFromSingleHost() throws Exception {
DHTNodeFetcher nodeFetcher = dhtNodeFetcherFactory.createNodeFetcher(dhtBootstrapper);
nodeFetcher.start();
//request hosts
InetSocketAddress addr = new InetSocketAddress("127.0.0.1", UDP_ACCESS[0].getLocalPort());
nodeFetcher.requestDHTHosts(addr);
byte[] datagramBytes = new byte[1000];
DatagramPacket pack = new DatagramPacket(datagramBytes, 1000);
UDP_ACCESS[0].receive(pack);
InputStream in = new ByteArrayInputStream(pack.getData());
Message m = messageFactory.read(in, Network.TCP);
m.hop();
assertInstanceof(PingRequest.class, m);
PingRequest ping = (PingRequest)m;
assertTrue(ping.requestsDHTIPP());
//should not send another request until we get the pong
nodeFetcher.requestDHTHosts(addr);
try{
datagramBytes = new byte[1000];
pack = new DatagramPacket(datagramBytes, 1000);
UDP_ACCESS[0].receive(pack);
fail("Shouldn't have received a packet");
}catch(SocketTimeoutException ste) {}
//send the pong now
IpPortImpl ipp = new IpPortImpl("213.0.0.1", 1111);
PingReply reply = injector.getInstance(PingReplyFactory.class).create(ping.getGUID(), (byte)1, IpPort.EMPTY_LIST,
Arrays.asList(ipp));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
reply.write(baos);
pack = new DatagramPacket(baos.toByteArray(),
baos.toByteArray().length,
new InetSocketAddress("localhost", injector.getInstance(NetworkManager.class).getPort()));
UDP_ACCESS[0].send(pack);
//test the processing
Thread.sleep(1000);
Set<SocketAddress> hosts = dhtBootstrapper.getBootstrapHosts();
assertTrue(hosts.iterator().hasNext());
assertEquals(ipp.getInetAddress(), ((InetSocketAddress)hosts.iterator().next()).getAddress());
//now we should be able to send a ping again
datagramBytes = new byte[1000];
pack = new DatagramPacket(datagramBytes, 1000);
addr = new InetSocketAddress("127.0.0.1", UDP_ACCESS[1].getLocalPort());
nodeFetcher.requestDHTHosts(addr);
UDP_ACCESS[1].receive(pack);
in = new ByteArrayInputStream(pack.getData());
m = messageFactory.read(in, Network.TCP);
m.hop();
assertInstanceof(PingRequest.class, m);
ping = (PingRequest)m;
assertTrue(ping.requestsDHTIPP());
}
public void testRequestMultipleTimesFromSingleHost() throws Exception {
DHTNodeFetcher nodeFetcher = dhtNodeFetcherFactory.createNodeFetcher(dhtBootstrapper);
// try to send a ping, unregister it and send another ping
nodeFetcher.setPingExpireTime(100);
//wait: LISTEN_EXPIRE_TIME is not volatile and a wrong value may be read otherwise
Thread.sleep(1000);
byte[] datagramBytes = new byte[1000];
DatagramPacket pack = new DatagramPacket(datagramBytes, 1000);
InetSocketAddress addr = new InetSocketAddress("127.0.0.1", UDP_ACCESS[2].getLocalPort());
nodeFetcher.requestDHTHosts(addr);
Thread.sleep(500);
datagramBytes = new byte[1000];
pack = new DatagramPacket(datagramBytes, 1000);
addr = new InetSocketAddress("127.0.0.1", UDP_ACCESS[2].getLocalPort());
nodeFetcher.requestDHTHosts(addr);
UDP_ACCESS[2].receive(pack);
InputStream in = new ByteArrayInputStream(pack.getData());
Message m = messageFactory.read(in, Network.TCP);
m.hop();
assertInstanceof(PingRequest.class, m);
PingRequest ping = (PingRequest)m;
assertTrue(ping.requestsDHTIPP());
}
public void testAddHostCatcherActiveNodes() throws Exception {
PrivilegedAccessor.setValue(DHTSettings.DHT_NODE_FETCHER_TIME, "value", 100L);
dhtBootstrapper.setWaitingForNodes(true);
DHTNodeFetcher nodeFetcher = dhtNodeFetcherFactory.createNodeFetcher(dhtBootstrapper);
HostCatcher hostCatcher = injector.getInstance(HostCatcher.class);
for(int i=0; i < UDP_ACCESS.length; i++) {
ExtendedEndpoint ep = new ExtendedEndpoint(
"127.0.0.1",
UDP_ACCESS[i].getLocalPort());
ep.setDHTVersion(0);
ep.setDHTMode(DHTMode.ACTIVE);
hostCatcher.add(ep, false);
}
nodeFetcher.start();
Thread.sleep(500);
Set<SocketAddress> hosts = dhtBootstrapper.getBootstrapHosts();
assertEquals(UDP_ACCESS.length, hosts.size());
}
public void testRequestDHTHostsFromHostCatcher() throws Exception{
PrivilegedAccessor.setValue(DHTSettings.DHT_NODE_FETCHER_TIME, "value", 100L);
dhtBootstrapper.setWaitingForNodes(true);
DHTNodeFetcher nodeFetcher = dhtNodeFetcherFactory.createNodeFetcher(dhtBootstrapper);
HostCatcher hostCatcher = injector.getInstance(HostCatcher.class);
for(int i=0; i < UDP_ACCESS.length; i++) {
ExtendedEndpoint ep = new ExtendedEndpoint(
"127.0.0.1",
UDP_ACCESS[i].getLocalPort());
ep.setDHTVersion(0);
ep.setDHTMode(DHTMode.PASSIVE);
hostCatcher.add(ep, false);
}
nodeFetcher.start();
Thread.sleep(1000);
byte[] datagramBytes;
DatagramPacket pack;
InputStream in;
Message m;
PingRequest ping;
for(int i=0; i < UDP_ACCESS.length; i++) {
datagramBytes = new byte[1000];
pack = new DatagramPacket(datagramBytes, 1000);
UDP_ACCESS[i].receive(pack);
in = new ByteArrayInputStream(pack.getData());
m = messageFactory.read(in, Network.TCP);
m.hop();
assertInstanceof(PingRequest.class, m);
ping = (PingRequest)m;
assertTrue(ping.requestsDHTIPP());
}
}
private class DHTBootstrapperStub implements DHTBootstrapper {
private boolean isWaitingForNodes;
private Set<SocketAddress> bootstrapHosts;
public DHTBootstrapperStub() {
bootstrapHosts = new FixedSizeLIFOSet<SocketAddress>(10, EjectionPolicy.FIFO);
}
public void addBootstrapHost(SocketAddress hostAddress) {
bootstrapHosts.add(hostAddress);
}
public void addPassiveNode(SocketAddress hostAddress) {
}
public void bootstrap() {
}
public boolean isBootstrappingFromRT() {
return false;
}
public boolean isWaitingForNodes() {
return isWaitingForNodes;
}
public void stop() {
}
public void setWaitingForNodes(boolean waiting) {
isWaitingForNodes = waiting;
}
public Set<SocketAddress> getBootstrapHosts() {
return bootstrapHosts;
}
}
}