/*
* Tests the server-side code for the udp crawler ping
*/
package com.limegroup.gnutella.messages.vendor;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import junit.framework.Test;
import com.limegroup.gnutella.Connection;
import com.limegroup.gnutella.Constants;
import com.limegroup.gnutella.CountingConnection;
import com.limegroup.gnutella.Endpoint;
import com.limegroup.gnutella.ExtendedEndpoint;
import com.limegroup.gnutella.GUID;
import com.limegroup.gnutella.RouterService;
import com.limegroup.gnutella.UDPService;
import com.limegroup.gnutella.handshaking.LeafHeaders;
import com.limegroup.gnutella.handshaking.UltrapeerHeaders;
import com.limegroup.gnutella.messages.BadPacketException;
import com.limegroup.gnutella.messages.Message;
import com.limegroup.gnutella.settings.ApplicationSettings;
import com.limegroup.gnutella.settings.ConnectionSettings;
import com.limegroup.gnutella.settings.FilterSettings;
import com.limegroup.gnutella.settings.UltrapeerSettings;
import com.limegroup.gnutella.stubs.ActivityCallbackStub;
import com.limegroup.gnutella.util.BaseTestCase;
import com.limegroup.gnutella.util.CommonUtils;
import com.limegroup.gnutella.util.EmptyResponder;
import com.limegroup.gnutella.util.FixedSizeExpiringSet;
import com.limegroup.gnutella.util.PrivilegedAccessor;
/**
* how it works:
*
* Leaf 1 -----------\ /-------Ultrapeer 1
* Leaf 2 -------- Ultrapeer 0-------Ultrapeer 2
* Leaf 3 ----------/ /|\ \-------Ultrapeer 3
* |
* Leaf 0 -- (GiveUPList)
*
* Ultrapeer 0 is connected to 3 other Ultrapeers and 3 leafs.
* Leaf 0 sends various GiveUPListVendorMessages and expects to
* receive various results.
*/
public class ServerSideUPListTest extends BaseTestCase {
/**
* The port that the central Ultrapeer listens on, and that the other nodes
* connect to it on.
*/
private static final int PORT = 6669;
private static InetAddress _udpAddress;
private static int _udpPort;
/**
* The timeout value for sockets -- how much time we wait to accept
* individual messages before giving up.
*/
private static final int TIMEOUT = 2000;
/**
* The different connections to the ultrapeer.
*/
private static CountingConnection LEAF_1,LEAF_2, LEAF_3, UP1, UP2, UP3;
/**
* the different kinds of queries
*/
private static final UDPCrawlerPing msgAll = new UDPCrawlerPing(new GUID(GUID.makeGuid()));
private static final UDPCrawlerPing msgSome = new UDPCrawlerPing(new GUID(GUID.makeGuid()), 2, 1,(byte)0);
private static final UDPCrawlerPing msgLeafsOnly = new UDPCrawlerPing(new GUID(GUID.makeGuid()), 0, 2,(byte)0);
private static final UDPCrawlerPing msgNone = new UDPCrawlerPing(new GUID(GUID.makeGuid()), 0, 0,(byte)0);
private static final UDPCrawlerPing msgMore = new UDPCrawlerPing(new GUID(GUID.makeGuid()), 20, 30,(byte)0);
private static final UDPCrawlerPing msgTimes = new UDPCrawlerPing(new GUID(GUID.makeGuid()),3,3,(byte)1);
private static final UDPCrawlerPing msgLocale = new UDPCrawlerPing(new GUID(GUID.makeGuid()),3,3,(byte)2);
private static final UDPCrawlerPing msgBadMask = new UDPCrawlerPing(new GUID(GUID.makeGuid()),3,3,(byte)0xFF);
private static final UDPCrawlerPing msgNewOnly = new UDPCrawlerPing(new GUID(GUID.makeGuid()),3,3,(byte)0x4);
private static final UDPCrawlerPing msgAgents = new UDPCrawlerPing(new GUID(GUID.makeGuid()),3,3,(byte)0x8);
/**
* The central Ultrapeer used in the test.
*/
private static final RouterService ROUTER_SERVICE =
new RouterService(new ActivityCallbackStub());
/**
* Ultrapeer 1 UDP connection.
*/
private static DatagramSocket UDP_ACCESS;
public ServerSideUPListTest(String name) {
super(name);
}
public static Test suite() {
return buildTestSuite(ServerSideUPListTest.class);
}
public static void main(String[] args) {
junit.textui.TestRunner.run(suite());
}
/**
* builds the connections between the entities in the test
* @throws Exception something bad happened (?)
*/
private static void buildConnections() throws Exception {
LEAF_1 = new CountingConnection("localhost", PORT);
LEAF_2 = new CountingConnection("localhost", PORT);
LEAF_3 = new CountingConnection("localhost", PORT);
UP1 = new CountingConnection("localhost", PORT);
UP2 = new CountingConnection("localhost", PORT);
UP3 = new CountingConnection("localhost", PORT);
UDP_ACCESS = new DatagramSocket();
}
public static void setSettings() {
String localIP = null;
try {
localIP = InetAddress.getLocalHost().getHostAddress();
}
catch (Exception ignored) {}
FilterSettings.BLACK_LISTED_IP_ADDRESSES.setValue(
new String[] {"*.*.*.*"});
FilterSettings.WHITE_LISTED_IP_ADDRESSES.setValue(
new String[] {localIP,"127.*.*.*"});
ConnectionSettings.PORT.setValue(PORT);
UltrapeerSettings.EVER_ULTRAPEER_CAPABLE.setValue(true);
UltrapeerSettings.DISABLE_ULTRAPEER_MODE.setValue(false);
UltrapeerSettings.FORCE_ULTRAPEER_MODE.setValue(true);
UltrapeerSettings.MAX_LEAVES.setValue(30);
ConnectionSettings.NUM_CONNECTIONS.setValue(30);
ConnectionSettings.LOCAL_IS_PRIVATE.setValue(false);
ConnectionSettings.USE_GWEBCACHE.setValue(false);
ConnectionSettings.WATCHDOG_ACTIVE.setValue(false);
}
public static void globalSetUp() throws Exception {
setSettings();
assertEquals("unexpected port", PORT,
ConnectionSettings.PORT.getValue());
RouterService.setListeningPort(PORT);
ROUTER_SERVICE.start();
ROUTER_SERVICE.clearHostCatcher();
ROUTER_SERVICE.connect();
connect();
assertEquals("unexpected port", PORT,
ConnectionSettings.PORT.getValue());
PrivilegedAccessor.setValue(RouterService.getPromotionManager(),
"_UDPListRequestors",
new FixedSizeExpiringSet(200,200));
UDP_ACCESS.connect(InetAddress.getLocalHost(),PORT);
}
public void setUp() {
setSettings();
}
public static void globalTearDown() throws Exception {
ROUTER_SERVICE.disconnect();
sleep();
LEAF_1.close();
LEAF_2.close();
LEAF_3.close();
UP1.close();
UP2.close();
UP3.close();
sleep();
}
private static void sleep() {
try {Thread.sleep(300);}catch(InterruptedException e) {}
}
/**
* Connects all of the nodes to the central test Ultrapeer.
*/
private static void connect() throws Exception {
buildConnections();
//ultrapeers
UP1.initialize(new UltrapeerHeaders("localhost"), new EmptyResponder());
UP2.initialize(new UltrapeerHeaders("localhost"), new EmptyResponder());
UP3.initialize(new UltrapeerHeaders("localhost"), new EmptyResponder());
//leafs
LEAF_1.initialize(new LeafHeaders("localhost"),new EmptyResponder());
LEAF_2.initialize(new LeafHeaders("localhost"), new EmptyResponder());
LEAF_3.initialize(new LeafHeaders("localhost"), new EmptyResponder());
assertTrue("ULTRAPEER_2 should be connected", UP2.isOpen());
assertTrue("ULTRAPEER_1 should be connected", UP1.isOpen());
assertTrue("ULTRAPEER_3 should be connected", UP3.isOpen());
assertTrue("LEAF should be connected", LEAF_1.isOpen());
assertTrue("LEAF should be connected", LEAF_2.isOpen());
assertTrue("LEAF should be connected", LEAF_3.isOpen());
// make sure we get rid of any initial ping pong traffic exchanges
sleep();
drainAll();
sleep();
}
/**
* Drains all messages
*/
private static void drainAll() throws Exception {
if(UP1.isOpen()) {
drain(UP1);
}
if(UP2.isOpen()) {
drain(UP2);
}
if(UP3.isOpen()){
drain(UP3);
}
if(LEAF_1.isOpen()) {
drain(LEAF_1);
}
if(LEAF_2.isOpen()) {
drain(LEAF_2);
}
if(LEAF_3.isOpen()) {
drain(LEAF_3);
}
}
/**
* sends out a message requesting all leafs + ups
* @throws Exception
*/
public void testMsgAll() throws Exception {
UDPCrawlerPong reply = tryMessage(msgAll);
//test whether we got proper # of results
assertEquals(3,reply.getLeaves().size());
assertEquals(3,reply.getUltrapeers().size());
assertEquals(LEAF_1.getInetAddress(),LEAF_2.getInetAddress());
assertEquals(LEAF_1.getInetAddress(),LEAF_3.getInetAddress());
assertEquals(UP1.getInetAddress(),UP2.getInetAddress());
assertEquals(UP1.getInetAddress(),UP3.getInetAddress());
for (Iterator iter = reply.getLeaves().iterator();iter.hasNext();){
Endpoint e =(Endpoint)iter.next();
assertEquals(LEAF_1.getInetAddress(),e.getInetAddress());
assertTrue(e.getPort() == LEAF_1.getPort());
}
for (Iterator iter = reply.getUltrapeers().iterator();iter.hasNext();){
Endpoint e =(Endpoint)iter.next();
assertEquals(UP1.getInetAddress(),e.getInetAddress());
assertTrue(e.getPort() == UP1.getPort());
}
sleep();
}
/**
* sends a message requesting 0 leafs and 0 ups.
*/
public void testMsgNone() throws Exception {
UDPCrawlerPong reply = tryMessage(msgNone);
assertEquals(0,reply.getLeaves().size());
assertEquals(0,reply.getUltrapeers().size());
sleep();
}
/**
* sends a message requesting leafs only
*/
public void testMsgLeafs() throws Exception {
UDPCrawlerPong reply = tryMessage(msgLeafsOnly);
assertEquals(2,reply.getLeaves().size());
assertEquals(0,reply.getUltrapeers().size());
sleep();
}
/**
* sends a message requesting 1 leafs and 2 ups.
*/
public void testMsgSome() throws Exception {
UDPCrawlerPong reply = tryMessage(msgSome);
assertEquals(1,reply.getLeaves().size());
assertEquals(2,reply.getUltrapeers().size());
sleep();
}
/**
* sends a message requesting more UPs and Leafs that the host has.
*/
public void testMsgMore() throws Exception {
UDPCrawlerPong reply = tryMessage(msgMore);
//we should get the number we have connected.
assertEquals(3,reply.getLeaves().size());
assertEquals(3,reply.getUltrapeers().size());
sleep();
}
/**
* requests all connections of a node; few of them disconnect
* and then they are requested again.
*
public void testAllDisconnectAllAgain() throws Exception {
LEAF_2.close();
LEAF_3.close();
UP2.close();
sleep();
UDPCrawlerPong reply = tryMessage(msgAll);
assertEquals(2,reply.getUltrapeers().size());
assertEquals(1,reply.getLeaves().size());
sleep();
}
/**
* tests whether requesting too often will give us a reply
*/
public void testHammering() throws Exception {
//first message should go through
UDPCrawlerPong reply = tryMessage(msgAll);
//second shouldn't
try {
reply = tryMessage(msgAll);
fail("ioex expected");
}catch (IOException iox) {}
sleep();
//third should
reply = tryMessage(msgAll);
assertNotEquals(0,reply.getLeaves().size());
assertNotEquals(0,reply.getUltrapeers().size());
sleep();
}
/**
* tests a udp ping message requesting the connection lifetimes
*/
public void testConnectionTime() throws Exception{
PrivilegedAccessor.setValue(Constants.class,"MINUTE",new Long(1));
UDPCrawlerPong reply = tryMessage(msgTimes);
assertTrue(reply.hasConnectionTime());
assertFalse(reply.hasLocaleInfo());
//see if the result we got had any uptime (it should!)
ExtendedEndpoint result = (ExtendedEndpoint)reply.getUltrapeers().get(0);
assertGreaterThan(0,result.getDailyUptime());
sleep();
}
/**
* tests a message send from a newer peer that supports more options.
*/
public void testBadMask() throws Exception {
PrivilegedAccessor.setValue(Constants.class,"MINUTE",new Long(1));
UDPCrawlerPong reply = tryMessage(msgBadMask);
assertTrue(reply.hasConnectionTime());
assertTrue(reply.hasLocaleInfo());
//see if the result we got had any uptime (it should!)
ExtendedEndpoint result = (ExtendedEndpoint)reply.getUltrapeers().get(0);
assertGreaterThan(0,result.getDailyUptime());
sleep();
}
/**
* tests a ping message requesting locale info.
*/
public void testLocale() throws Exception {
PrivilegedAccessor.setValue(Constants.class,"MINUTE",new Long(1));
UDPCrawlerPong reply = tryMessage(msgLocale);
assertFalse(reply.hasConnectionTime());
assertTrue(reply.hasLocaleInfo());
//see if any of the connections have the locale in them - they should
ExtendedEndpoint result = (ExtendedEndpoint)reply.getUltrapeers().get(0);
assertEquals(ApplicationSettings.LANGUAGE.getValue(),
result.getClientLocale());
sleep();
}
/**
* tests a ping message requesting only results that support
* udp crawling themselves.
*/
public void testNewOnly() throws Exception {
PrivilegedAccessor.setValue(Constants.class,"MINUTE",new Long(1));
//make sure some of our connections support crawling.
MessagesSupportedVendorMessage msvm = (MessagesSupportedVendorMessage)
PrivilegedAccessor.invokeConstructor(MessagesSupportedVendorMessage.class, new Object[0]);
assertGreaterThan(0,msvm.supportsUDPCrawling());
List upCons = RouterService.getConnectionManager().getInitializedConnections();
Connection notSupporting = (Connection) upCons.get(0);
Connection supporting = (Connection) upCons.get(1);
assertLessThanOrEquals(1,notSupporting.remoteHostSupportsUDPCrawling());
assertLessThanOrEquals(1,supporting.remoteHostSupportsUDPCrawling());
PrivilegedAccessor.setValue(supporting,"_messagesSupported",msvm);
assertLessThanOrEquals(1,notSupporting.remoteHostSupportsUDPCrawling());
assertGreaterThanOrEquals(1,supporting.remoteHostSupportsUDPCrawling());
//so now, only one UP should be in the result.
UDPCrawlerPong pong = tryMessage(msgNewOnly);
assertEquals(1, pong.getUltrapeers().size());
sleep();
//now, make one other UP support that message as well
PrivilegedAccessor.setValue(notSupporting,"_messagesSupported",msvm);
assertGreaterThan(0,notSupporting.remoteHostSupportsUDPCrawling());
assertGreaterThan(0,supporting.remoteHostSupportsUDPCrawling());
pong = tryMessage(msgNewOnly);
assertEquals(2,pong.getUltrapeers().size());
sleep();
}
public void testMsgAgents() throws Exception {
PrivilegedAccessor.setValue(Constants.class,"MINUTE",new Long(1));
UDPCrawlerPong pong = tryMessage(msgAgents);
assertNotNull(pong.getAgents());
//we should have 3 agents reported.
StringTokenizer tok = new StringTokenizer(pong.getAgents(),
UDPCrawlerPong.AGENT_SEP);
assertEquals(7,tok.countTokens());
while(tok.hasMoreTokens())
assertEquals(CommonUtils.getHttpServer(),tok.nextToken());
}
private UDPCrawlerPong tryMessage(UDPCrawlerPing which) throws Exception {
assertTrue(UDPService.instance().isListening());
UDP_ACCESS.setSoTimeout(5000);
_udpAddress = UDP_ACCESS.getLocalAddress();
//send a packet
ByteArrayOutputStream baos = new ByteArrayOutputStream();
which.write(baos);
DatagramPacket pack = new DatagramPacket(baos.toByteArray(),
baos.toByteArray().length,
_udpAddress, PORT);
assertNotNull(baos.toByteArray());
assertNotNull(_udpAddress);
UDP_ACCESS.send(pack);
//now read the response
//_udpPort = UDP_ACCESS.getLocalPort();
pack = new DatagramPacket(new byte[1000],1000);
//not catching IOEx here because not replying is a valid scenario.
UDP_ACCESS.receive(pack);
//parse the response
InputStream in = new ByteArrayInputStream(pack.getData());
UDPCrawlerPong reply = null;
try {
reply = (UDPCrawlerPong)Message.read(in);
}catch(BadPacketException bad) {
System.out.println("the size of the received response is "+
pack.getData().length);
fail("could not parse the response",bad);
} catch (ClassCastException ccx) {
System.out.println(new String(pack.getData()));
fail("parsed to a wrong packet type",ccx);
}
//then test whether the guids are the same
assertEquals(new String(reply.getGUID()),new String(which.getGUID()));
return reply;
}
}