package com.limegroup.gnutella; import java.io.File; import java.util.Arrays; import java.util.Iterator; import junit.framework.Test; import com.limegroup.gnutella.handshaking.LeafHeaders; import com.limegroup.gnutella.handshaking.UltrapeerHeaders; import com.limegroup.gnutella.messages.Message; import com.limegroup.gnutella.messages.PingReply; import com.limegroup.gnutella.messages.PushRequest; import com.limegroup.gnutella.messages.QueryReply; import com.limegroup.gnutella.messages.QueryRequest; import com.limegroup.gnutella.routing.QueryRouteTable; import com.limegroup.gnutella.routing.RouteTableMessage; import com.limegroup.gnutella.settings.ConnectionSettings; import com.limegroup.gnutella.settings.FilterSettings; import com.limegroup.gnutella.settings.PingPongSettings; import com.limegroup.gnutella.settings.SharingSettings; import com.limegroup.gnutella.settings.UltrapeerSettings; import com.limegroup.gnutella.stubs.ActivityCallbackStub; import com.limegroup.gnutella.util.BaseTestCase; import com.limegroup.gnutella.util.EmptyResponder; /** * The most important end-to-end message routing test. Checks whether * ultrapeers handle query routing, normal routing, routing of marked pongs, * etc. The test is structured with one Ultrapeer connected to two other * Ultrapeers as well as to a leaf. The leaves and two Ultrapeers pass * various messages to each other, and the tests verify that the correct messages * are received by the other nodes. This is shown in the diagram below: * * ULTRAPEER_1 ---- CENTRAL TEST ULTRAPEER ---- ULTRAPEER_2 * | * | * | * LEAF */ public final class UltrapeerRoutingTest extends BaseTestCase { /** * Simple non blank IP. */ private static final byte[] IP = new byte[] { 1, 1, 1, 1 }; /** * The port that the central Ultrapeer listens on, and that the other nodes * connect to it on. */ private static final int PORT = 6667; /** * The timeout value for sockets -- how much time we wait to accept * individual messages before giving up. */ private static final int TIMEOUT = 1800; /** * The default TTL to use for request messages. */ private final static byte TTL = 7; /** * The "soft max" TTL used by LimeWire's message routing -- hops + ttl * greater than this value have their TTLs automatically reduced. * * NOTE: This is now based on the X-Max-TTL header for these tests, * since our connectin pass this headers, and this is what the * per-connection soft max is based on. */ private static final byte SOFT_MAX = (byte)4; // X-Max-TTL+1 /** * The TTL of the initial "probe" queries that the Ultrapeer uses to * determine how widely distributed a file is. */ private static final byte PROBE_QUERY_TTL = 2; /** * Leaf connection to the Ultrapeer. */ private Connection LEAF; /** * Ultrapeer connection. */ private Connection ULTRAPEER_1; /** * Second Ultrapeer connection */ private Connection ULTRAPEER_2; /** * The central Ultrapeer used in the test. */ private static final RouterService ROUTER_SERVICE = new RouterService(new ActivityCallbackStub()); public UltrapeerRoutingTest(String name) { super(name); } public static Test suite() { return buildTestSuite(UltrapeerRoutingTest.class); } public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } private void buildConnections() { LEAF = new Connection("localhost", PORT); ULTRAPEER_1 = new Connection("localhost", PORT); ULTRAPEER_2 = new Connection("localhost", PORT); } public void setUp() throws Exception { //Setup LimeWire backend. For testing other vendors, you can skip all //this and manually configure a client to listen on port 6667, with //incoming slots and no connections. //To keep LimeWire from connecting to the outside network, we filter out //all addresses but localhost and 18.239.0.*. The latter is used in //pongs for testing. TODO: it would be nice to have a way to prevent //BootstrapServerManager from adding defaults and connecting. FilterSettings.BLACK_LISTED_IP_ADDRESSES.setValue( new String[] {"*.*.*.*"}); FilterSettings.WHITE_LISTED_IP_ADDRESSES.setValue( new String[] {"127.*.*.*", "18.239.0.*"}); ConnectionSettings.PORT.setValue(PORT); setSharedDirectories(new File[0]); ConnectionSettings.CONNECT_ON_STARTUP.setValue(false); UltrapeerSettings.EVER_ULTRAPEER_CAPABLE.setValue(true); UltrapeerSettings.DISABLE_ULTRAPEER_MODE.setValue(false); UltrapeerSettings.FORCE_ULTRAPEER_MODE.setValue(true); UltrapeerSettings.MAX_LEAVES.setValue(4); ConnectionSettings.NUM_CONNECTIONS.setValue(3); ConnectionSettings.LOCAL_IS_PRIVATE.setValue(false); ConnectionSettings.USE_GWEBCACHE.setValue(false); ConnectionSettings.WATCHDOG_ACTIVE.setValue(false); PingPongSettings.PINGS_ACTIVE.setValue(false); assertEquals("unexpected port", PORT, ConnectionSettings.PORT.getValue()); ROUTER_SERVICE.start(); ROUTER_SERVICE.clearHostCatcher(); ROUTER_SERVICE.connect(); connect(); assertEquals("unexpected port", PORT, ConnectionSettings.PORT.getValue()); } public void tearDown() throws Exception { drainAll(); sleep(); LEAF.close(); ULTRAPEER_1.close(); ULTRAPEER_2.close(); ROUTER_SERVICE.disconnect(); sleep(); } private void sleep() { try {Thread.sleep(300);}catch(InterruptedException e) {} } /** * Drains all messages */ private void drainAll() throws Exception { if(ULTRAPEER_1.isOpen()) { drain(ULTRAPEER_1); } if(ULTRAPEER_1.isOpen()) { drain(ULTRAPEER_2); } if(LEAF.isOpen()) { drain(LEAF); } } /** * Connects all of the nodes to the central test Ultrapeer. */ private void connect() throws Exception { buildConnections(); //1. first Ultrapeer connection ULTRAPEER_2.initialize(new UltrapeerHeaders("localhost"), new EmptyResponder()); //2. second Ultrapeer connection ULTRAPEER_1.initialize(new UltrapeerHeaders("localhost"), new EmptyResponder()); //3. routed leaf, with route table for "test" LEAF.initialize(new LeafHeaders("localhost"), new EmptyResponder()); assertTrue("ULTRAPEER_2 should be connected", ULTRAPEER_2.isOpen()); assertTrue("ULTRAPEER_1 should be connected", ULTRAPEER_1.isOpen()); assertTrue("LEAF should be connected", LEAF.isOpen()); QueryRouteTable qrt = new QueryRouteTable(); qrt.add("test"); qrt.add("susheel"); qrt.addIndivisible(HugeTestUtils.UNIQUE_SHA1.toString()); for (Iterator iter=qrt.encode(null).iterator(); iter.hasNext(); ) { LEAF.send((RouteTableMessage)iter.next()); LEAF.flush(); } // for Ultrapeer 1 qrt = new QueryRouteTable(); qrt.add("leehsus"); qrt.add("awesome"); for (Iterator iter=qrt.encode(null).iterator(); iter.hasNext(); ) { ULTRAPEER_1.send((RouteTableMessage)iter.next()); ULTRAPEER_1.flush(); } // make sure we get rid of any initial ping pong traffic exchanges sleep(); drainAll(); //sleep(); drainAll(); sleep(); drainAll(); } /** * Tests broadcasting of queries from ULTRAPEER_2. */ public void testBroadcastFromUltrapeer2() throws Exception { QueryRequest qr = QueryRequest.createQuery("crap"); ULTRAPEER_2.send(qr); ULTRAPEER_2.flush(); Message m = ULTRAPEER_1.receive(TIMEOUT); assertInstanceof("expected a query request", QueryRequest.class, m); QueryRequest qr2 = (QueryRequest)m; assertEquals("unexpected query", "crap", qr2.getQuery()); assertEquals("unexpected hops", (byte)1, m.getHops()); assertEquals("unexpected TTL", (byte)(SOFT_MAX-1), m.getTTL()); assertTrue("should not have drained leaf successfully", !drain(LEAF)); } /** * Tests a query sent from the leaf. The query should be received by both * Ultrapeer connections -- the one connected to the leaf, as well as the * other one. */ public void testBroadcastFromLeaf() throws Exception { //1. Check that query broadcasted to ULTRAPEER_2 and ultrapeer QueryRequest qr = QueryRequest.createQuery("crap"); LEAF.send(qr); LEAF.flush(); //Message m; Message m = ULTRAPEER_2.receive(TIMEOUT); assertQuery(m); assertEquals("unexpected query", "crap", ((QueryRequest)m).getQuery()); assertEquals("unexpected hops", (byte)1, m.getHops()); // since it's coming from the leaf, the intervening Ultrapeer // sends a dynamic, probe query -- so check for that TTL assertEquals("unexpected TTL", PROBE_QUERY_TTL, m.getTTL()); m = ULTRAPEER_1.receive(TIMEOUT); assertQuery(m); assertEquals("unexpected query", "crap", ((QueryRequest)m).getQuery()); assertEquals("unexpected hops", (byte)1, m.getHops()); // since it's coming from the leaf, the intervening Ultrapeer // sends a dynamic, probe query -- so check for that TTL assertEquals("unexpected TTL", PROBE_QUERY_TTL, m.getTTL()); //2. Check that replies are routed back. drain(LEAF); Response response1=new Response(0L, 0L, "response1.txt"); byte[] clientGUID = GUID.makeGuid(); QueryReply reply1=new QueryReply(qr.getGUID(), (byte)2, 6346, IP, 56, new Response[] {response1}, clientGUID, false); ULTRAPEER_2.send(reply1); ULTRAPEER_2.flush(); QueryReply replyRead=(QueryReply)LEAF.receive(TIMEOUT); assertTrue("guids should be equal", Arrays.equals(clientGUID, replyRead.getClientGUID())); drain(LEAF); Response response2 = new Response(0l, 0l, "response2.txt"); byte[] guid2 = GUID.makeGuid(); QueryReply reply2 = new QueryReply(qr.getGUID(), (byte)2, 6346, IP, 56, new Response[] {response1}, guid2, false); ULTRAPEER_1.send(reply2); ULTRAPEER_1.flush(); m = LEAF.receive(TIMEOUT); assertInstanceof("message not a QueryReply", QueryReply.class, m); replyRead = (QueryReply)m; assertTrue("guids should be equal", Arrays.equals(guid2, replyRead.getClientGUID())); //3. Check that pushes are routed (not broadcast) drain(ULTRAPEER_2); drain(ULTRAPEER_1); PushRequest push1 = new PushRequest(GUID.makeGuid(), (byte)2, clientGUID, 0, IP, 6346); LEAF.send(push1); LEAF.flush(); m = ULTRAPEER_2.receive(TIMEOUT); assertInstanceof("message not a PushRequest", PushRequest.class, m); PushRequest pushRead = (PushRequest)m; assertEquals("unexpected push index", 0, pushRead.getIndex()); assertTrue("should not have drained ULTRAPEER_1 successfully", !drain(ULTRAPEER_1)); // check that pushes with unmatching client guids are not forwarded PushRequest push2 = new PushRequest(GUID.makeGuid(),(byte)2, guid2, 1, IP, 6346); LEAF.send(push2); LEAF.flush(); m = ULTRAPEER_1.receive(TIMEOUT); assertInstanceof("message not a PushRequest", PushRequest.class, m); pushRead=(PushRequest)m; assertEquals("unexpected push index", 1,pushRead.getIndex()); assertTrue("should not have drained ultrapeer successfully", !drain(ULTRAPEER_2)); // Check that queries can re-route push routes drain(LEAF); drain(ULTRAPEER_2); ULTRAPEER_1.send(reply1); ULTRAPEER_1.flush(); m = LEAF.receive(TIMEOUT); assertInstanceof("message not a QueryReply", QueryReply.class, m); replyRead = (QueryReply)m; assertTrue("unexpected GUID", Arrays.equals(clientGUID, replyRead.getClientGUID())); PushRequest push3 = new PushRequest(GUID.makeGuid(), (byte)2, clientGUID, 3, IP, 6346); LEAF.send(push3); LEAF.flush(); m = ULTRAPEER_1.receive(TIMEOUT); assertInstanceof("message not a PushRequest", PushRequest.class, m); pushRead = (PushRequest)m; assertEquals("unexpected push index", 3, pushRead.getIndex()); assertTrue("should not have drained ultrapeer successfully", !drain(ULTRAPEER_2)); } /** * Tests URN queries from the leaf. */ public void testUrnQueryToLeaf() throws Exception { QueryRequest qr = QueryRequest.createQuery(HugeTestUtils.UNIQUE_SHA1); ULTRAPEER_2.send(qr); ULTRAPEER_2.flush(); Message m = LEAF.receive(TIMEOUT); assertQuery(m); QueryRequest qrRead = (QueryRequest)m; assertEquals("unexpected query", "\\", ((QueryRequest)m).getQuery()); assertTrue("guids should be equal", Arrays.equals(qr.getGUID(), qrRead.getGUID())); m = ULTRAPEER_1.receive(TIMEOUT); assertQuery(m); qrRead = (QueryRequest)m; assertEquals("unexpected query", "\\", ((QueryRequest)m).getQuery()); assertTrue("guids should be equal", Arrays.equals(qr.getGUID(), qrRead.getGUID())); } /** * Test to make sure that queries with one more hop to go are * properly routed when the last hop is an Ultrapeer. */ public void testLastHopQueryRouting() throws Exception { // first make sure it gets through on NOT last hop... QueryRequest qr = QueryRequest.createQuery("junkie junk", (byte)3); ULTRAPEER_2.send(qr); ULTRAPEER_2.flush(); Message m = ULTRAPEER_1.receive(TIMEOUT); assertQuery(m); QueryRequest qrRead = (QueryRequest)m; assertTrue("guids should be equal", Arrays.equals(qr.getGUID(), qrRead.getGUID())); // now make sure it doesn't get through on last hop qr = QueryRequest.createQuery("junkie junk", (byte)2); ULTRAPEER_2.send(qr); ULTRAPEER_2.flush(); assertTrue(!drain(ULTRAPEER_1)); // ok, now make sure a query DOES get through on the last hop qr = QueryRequest.createQuery("leehsu", (byte)2); testLastHop(ULTRAPEER_2, ULTRAPEER_1, qr); //qr = QueryRequest.createQuery("susheel", (byte)2); //testLastHop(ULTRAPEER_2, ULTRAPEER_1, qr); //testLastHop(ULTRAPEER_1, ULTRAPEER_2, qr); //qr = QueryRequest.createQuery("susheel", (byte)2); //testLastHop(ULTRAPEER_2, ULTRAPEER_1, qr); //testLastHop(ULTRAPEER_1, ULTRAPEER_2, qr); // old test // ULTRAPEER_2.send(qr); // ULTRAPEER_2.flush(); // m = ULTRAPEER_1.receive(TIMEOUT); // assertQuery(m); // qrRead = (QueryRequest)m; // assertTrue("guids should be equal", // Arrays.equals(qr.getGUID(), qrRead.getGUID())); // end old test // ok, now make sure a query DOES get through on the last hop // when the content is on the leaf /* qr = QueryRequest.createQuery("susheel", (byte)2); ULTRAPEER_2.send(qr); ULTRAPEER_2.flush(); m = ULTRAPEER_1.receive(TIMEOUT); assertQuery(m); qrRead = (QueryRequest)m; assertTrue("guids should be equal", Arrays.equals(qr.getGUID(), qrRead.getGUID())); */ } /** * Helper method that sends a query to that would be routed on the * last hop from the sender to the receiver. * * @param sender the <tt>Connection</tt> */ private static void testLastHop(Connection sender, Connection receiver, QueryRequest qr) throws Exception { sender.send(qr); sender.flush(); Message m = receiver.receive(TIMEOUT); assertQuery(m); QueryRequest qrRead = (QueryRequest)m; assertTrue("guids should be equal", Arrays.equals(qr.getGUID(), qrRead.getGUID())); } /** * Tests to make sure that queries by URN are correctly forwarded * only to those nodes that should receive them. In this case, for example, * the leaf and Ultrapeer routing tables have no URN data, so the leaf * should not receive the queries, and the Ultrapeer should not receive * them at the last hop. */ public void testThatURNOnlyQueryDoesNotGetImproperlyForwarded() throws Exception { QueryRequest qr = QueryRequest.createRequery(HugeTestUtils.SHA1); ULTRAPEER_1.send(qr); ULTRAPEER_1.flush(); Message m = ULTRAPEER_2.receive(TIMEOUT); assertQuery(m); QueryRequest qrRead = (QueryRequest)m; assertTrue("guids should be equal", Arrays.equals(qr.getGUID(), qrRead.getGUID())); assertTrue("leaf should not have received the query", !drain(LEAF)); // now test to make sure that query routing on the last hop // is working correctly for URN queries qr = QueryRequest.createRequery(HugeTestUtils.SHA1, (byte)2); ULTRAPEER_1.send(qr); ULTRAPEER_1.flush(); assertTrue("ultrapeer should not have received the query", !drain(ULTRAPEER_2)); } /** * Tests URN queries from the leaf. */ public void testUrnQueryFromLeaf() throws Exception { QueryRequest qr = QueryRequest.createRequery(HugeTestUtils.SHA1); LEAF.send(qr); LEAF.flush(); Message m = ULTRAPEER_1.receive(TIMEOUT); assertQuery(m); QueryRequest qrRead = (QueryRequest)m; assertTrue("guids should be equal", Arrays.equals(qr.getGUID(), qrRead.getGUID())); m = ULTRAPEER_2.receive(TIMEOUT); assertQuery(m); qrRead = (QueryRequest)m; assertTrue("guids should be equal", Arrays.equals(qr.getGUID(), qrRead.getGUID())); } /** * Tests the broadcasting of queries from ultrapeer 2 to the leaf. */ public void testBroadcastFromUltrapeer2ToLeaf() throws Exception { QueryRequest qr = QueryRequest.createQuery("test"); ULTRAPEER_2.send(qr); ULTRAPEER_2.flush(); Message m = ULTRAPEER_1.receive(TIMEOUT); assertInstanceof("expected a query request", QueryRequest.class, m); assertEquals("unexpected query", "test", ((QueryRequest)m).getQuery()); assertEquals("unexpected hops", (byte)1, m.getHops()); assertEquals("unexpected TTL", (byte)(SOFT_MAX-1), m.getTTL()); m = LEAF.receive(TIMEOUT); assertInstanceof("expected a query request", QueryRequest.class, m); assertEquals("unexpected query", "test", ((QueryRequest)m).getQuery()); assertEquals("unexpected hops", (byte)1, m.getHops()); assertEquals("unexpected TTL", (byte)(SOFT_MAX-1), m.getTTL()); } // private void addI18NToQRT() throws Exception { QueryRouteTable qrt = new QueryRouteTable(); qrt.add("\u4f5c\u540d"); qrt.add("g\u00E9\u00E9whiz"); //gee(e with acute)whiz qrt.add("\u4e3b\u6f14"); for(Iterator iter=qrt.encode(null).iterator(); iter.hasNext();) { LEAF.send((RouteTableMessage)iter.next()); LEAF.flush(); } qrt = new QueryRouteTable(); qrt.add("h\u00E8llo"); //he(e with grave)llo qrt.add("geewhiz"); qrt.add("\u30b9\u30bf\u30b8\u30aa"); for(Iterator iter=qrt.encode(null).iterator(); iter.hasNext();) { ULTRAPEER_1.send((RouteTableMessage)iter.next()); ULTRAPEER_1.flush(); } sleep(); drainAll(); drainAll(); sleep(); drainAll(); } // public void testI18NRouting() throws Exception { addI18NToQRT(); QueryRequest qr = QueryRequest.createQuery("hello", (byte)2); ULTRAPEER_2.send(qr); ULTRAPEER_2.flush(); //ultrapeer1 should get a query for hello since "h\u00e8llo" should //have the \u00e8 converted to an "e" when added to qrt for routing Message m = ULTRAPEER_1.receive(TIMEOUT); assertInstanceof("expected a query request", QueryRequest.class, m); assertEquals("unexpected query", "hello", ((QueryRequest)m).getQuery()); //shouldn't get to this leaf assertTrue(!drain(LEAF)); qr = QueryRequest.createQuery("\u4f5c\u540d", (byte)2); ULTRAPEER_2.send(qr); ULTRAPEER_2.flush(); m = LEAF.receive(TIMEOUT); assertInstanceof("expected a query request", QueryRequest.class, m); assertEquals("unexpected query", "\u4f5c\uu540d", ((QueryRequest)m).getQuery()); //shouldn't get to ultrapeer assertTrue(!drain(ULTRAPEER_1)); qr = QueryRequest.createQuery("\u30b9\u30bf\u30b8\u30aa", (byte)2); ULTRAPEER_2.send(qr); ULTRAPEER_2.flush(); m = ULTRAPEER_1.receive(TIMEOUT); assertInstanceof("expected a query request", QueryRequest.class, m); assertEquals("unexpected query", "\u30b9\u30bf\u30b8\u30aa", ((QueryRequest)m).getQuery()); //shouldn't get to LEAF assertTrue(!drain(LEAF)); //should get to both ULTRAPEER_1 and LEAF qr = QueryRequest.createQuery("geewhiz", (byte)2); ULTRAPEER_2.send(qr); ULTRAPEER_2.flush(); m = LEAF.receive(TIMEOUT); assertInstanceof("expected a query request", QueryRequest.class, m); assertEquals("unexpected query", "geewhiz", ((QueryRequest)m).getQuery()); m = ULTRAPEER_1.receive(TIMEOUT); assertInstanceof("expected a query request", QueryRequest.class, m); assertEquals("unexpected query", "geewhiz", ((QueryRequest)m).getQuery()); } /** * Tests broadcasting of queries from the Ultrapeer to other hosts. * In particular, this tests to make sure that the leaf correctly * receives the query. */ public void testBroadcastFromUltrapeerToBoth() throws Exception { QueryRequest qr= QueryRequest.createQuery("susheel test"); ULTRAPEER_1.send(qr); ULTRAPEER_1.flush(); Message m=ULTRAPEER_2.receive(TIMEOUT); assertInstanceof("expected a query request", QueryRequest.class, m); assertEquals("susheel test", ((QueryRequest)m).getQuery()); assertEquals("unexpected hops", (byte)1, m.getHops()); assertEquals("unexpected TTL", (byte)(SOFT_MAX-1), m.getTTL()); m=LEAF.receive(TIMEOUT); assertInstanceof("expected a query request", QueryRequest.class, m); assertEquals("unexpected query string", "susheel test", ((QueryRequest)m).getQuery()); assertEquals("unexpected hops", (byte)1, m.getHops()); assertEquals("unexpected TTL", (byte)(SOFT_MAX-1), m.getTTL()); } /** * Tests broadcasting pings between the various hosts. In particular, * this tests to make sure that leaves do not receive ping broadcasts. */ /* public void testPingBroadcast() throws Exception { //Send ping Message m=new PingRequest((byte)7); ULTRAPEER_1.send(m); ULTRAPEER_1.flush(); m=ULTRAPEER_2.receive(TIMEOUT); assertInstanceof("message should be a ping request", PingRequest.class, m); assertEquals("unexpected hops", (byte)1, m.getHops()); assertEquals("unexpected TTL", (byte)(SOFT_MAX-1), m.getTTL()); assertTrue("Leaf should not have received message", !drain(LEAF)); //Send reply drain(ULTRAPEER_1); PingReply pong = PingReply.create(m.getGUID(), (byte)7, 6344, new byte[4]); ULTRAPEER_2.send(pong); ULTRAPEER_2.flush(); for (int i=0; i<10; i++) { PingReply pongRead=(PingReply)ULTRAPEER_1.receive(TIMEOUT); if (pongRead.getPort()==pong.getPort()) return; } fail("Pong wasn't routed"); } */ /** * Tests the broadcasting of big pings -- pings that include GGEP extensions, * and so have a payload -- between the various hosts. */ /* public void testBigPingBroadcast() throws Exception { //1a. Send big ping (not GGEP...which should be ok) byte[] payload= new byte[16]; byte c = 65; //'A' for(int i=0;i<16;i++, c++) payload[i] = c; Message m=new PingRequest(GUID.makeGuid(), (byte)7, (byte)0,payload); LEAF.send(m); LEAF.flush(); //1b. Make sure ultrapeer gets it with payload. m=ULTRAPEER_1.receive(TIMEOUT); PingRequest ping = null; try{ ping = (PingRequest)m; }catch(ClassCastException cce){ fail("Big ping not created properly on ULTRAPEER_2 client", cce); } assertEquals("unexpected hops", (byte)1, m.getHops()); assertEquals("unexpected TTL", (byte)(SOFT_MAX-1), m.getTTL()); assertEquals("unexpected message length", 16, m.getLength()); //assertTrue(m.getLength()==16); //lets make sure the payload got there OK ByteArrayOutputStream stream = new ByteArrayOutputStream(); try{ ping.write(stream); }catch(IOException ioe){ fail("error while writing payload in ULTRAPEER_2 client", ioe); } byte[] b = stream.toByteArray(); //get rid of bytes 0-22(inclusive) ie the header String out = new String(b,23,b.length-23); assertEquals("wrong payload in ULTRAPEER_2 client", "ABCDEFGHIJKLMNOP", out); //1c. Make sure ULTRAPEER_2 also gets it with payload, as ULTRAPEER_2 is now also // an ultrapeer m=ULTRAPEER_2.receive(TIMEOUT); ping = null; try{ ping = (PingRequest)m; }catch(ClassCastException cce){ fail("Big ping not created properly on ULTRAPEER_2 client", cce); } assertEquals("unexpected hops", (byte)1, m.getHops()); assertEquals("unexpected TTL", (byte)(SOFT_MAX-1), m.getTTL()); //assertEquals("unexpected message length", 0, m.getLength()); assertEquals("unexpected message length", 16, m.getLength()); //2a. Send reply from ultrapeer //create payload for big pong byte[] payload2 = new byte[14+2]; //add the port payload2[0] = 0x0F; payload2[1] = 0x00;//port payload2[2] = 0x10; payload2[3] = 0x10; payload2[4] = 0x10; payload2[5] = 0x10;//ip = 16.16.16.16 payload2[6] = 0x0F;// payload2[7] = 0x00;// payload2[8] = 0x00;// payload2[9] = 0x00;//15 files shared payload2[10] = 0x0F;// payload2[11] = 0x00;// payload2[12] = 0x00;// payload2[13] = 0x00;//15 KB //OK Now for the big pong part payload2[14] = (byte) 65; payload2[15] = (byte) 66; drain(LEAF); PingReply pong = PingReply.createFromNetwork(m.getGUID(), (byte)7, (byte)0, payload2); ULTRAPEER_1.send(pong); ULTRAPEER_1.flush(); //2b. Make sure leaf reads it. PingReply ourPong = null; for (int i=0; i<10; i++) { PingReply pongRead=(PingReply)LEAF.receive(TIMEOUT); if (pongRead.getPort()==pong.getPort()){ ourPong = pongRead; break; } } assertNotNull("Pong wasn't routed", ourPong); //Lets check that the pong came back in good shape assertEquals("wrong port", 15, ourPong.getPort()); String ip = ourPong.getIP(); assertEquals("wrong IP", "16.16.16.16", ip); assertEquals("wrong files", 15, ourPong.getFiles()); assertEquals("Wrong share size", 15, ourPong.getKbytes()); stream = new ByteArrayOutputStream(); try{ ourPong.write(stream); }catch(IOException ioe){ fail("problem with writing out big pong", ioe); } byte[] op = stream.toByteArray(); byte[] big = new byte[2]; big[0] = op[op.length-2]; big[1] = op[op.length-1]; out = "";//reset out = new String(big); assertEquals("Big part of pong lost", "AB", out); } */ /** * Tests to make sure that pongs that had no entry in the routing * tables (that had no corresponding ping) are not forwarded. */ public void testMisroutedPong() throws Exception { Message m = PingReply.createExternal(GUID.makeGuid(), (byte)6, 7399, IP, false); ULTRAPEER_1.send(m); ULTRAPEER_1.flush(); assertTrue("should not have drained ultrapeer successfully", !drain(ULTRAPEER_2)); assertTrue("should not have drained leaf successfully", !drain(LEAF)); } /** * Tests that Ultrapeer pongs are correctly sent to leaves to * provide them with distributed host data. */ public void testUltrapeerPong() throws Exception { byte[] guid=GUID.makeGuid(); byte[] ip={(byte)18, (byte)239, (byte)0, (byte)143}; Message m = PingReply.createExternal(guid, (byte)7, 7399, ip, true); ULTRAPEER_1.send(m); ULTRAPEER_1.flush(); m=LEAF.receive(TIMEOUT); assertInstanceof("expected a pong", PingReply.class, m); assertEquals("unexpected port", 7399, ((PingReply)m).getPort()); assertTrue("should not have drained ultrapeer successfully", !drain(ULTRAPEER_2)); } /** * Tests that duplicate queries are not forwarded if the connection that * originated the connection is dropped. */ public void testDropAndDuplicate() throws Exception { //Send query request from leaf, received by ultrapeer (and ULTRAPEER_2) QueryRequest qr = QueryRequest.createQuery("crap"); LEAF.send(qr); LEAF.flush(); Message m=ULTRAPEER_1.receive(TIMEOUT); assertInstanceof("expected a QueryRequest", QueryRequest.class, m); assertEquals("unexpected query", "crap", ((QueryRequest)m).getQuery()); assertEquals("unexpected hops", (byte)1, m.getHops()); // since it's coming from the leaf, the intervening Ultrapeer // sends a dynamic, probe query -- so check for that TTL assertEquals("unexpected TTL", PROBE_QUERY_TTL, m.getTTL()); //After closing leaf (give it some time to clean up), make sure //duplicate query is dropped. drain(ULTRAPEER_1); LEAF.close(); try { Thread.sleep(200); } catch (InterruptedException e) { } ULTRAPEER_2.send(qr); ULTRAPEER_2.flush(); assertTrue("should not have drained ultrapeer successfully", !drain(ULTRAPEER_1)); } /** * Asserts that the given message is a query, printing out the * message and failing if it's not. * * @param m the <tt>Message</tt> to check */ private static void assertQuery(Message m) { if(m instanceof QueryRequest) return; System.out.println(m); assertInstanceof("message not a QueryRequest", QueryRequest.class, m); } }