package com.limegroup.gnutella; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.OutputStream; import java.util.HashSet; import java.util.Set; import junit.framework.Test; import com.limegroup.gnutella.altlocs.AlternateLocation; import com.limegroup.gnutella.altlocs.AlternateLocationCollection; import com.limegroup.gnutella.messages.GGEP; import com.limegroup.gnutella.util.PrivilegedAccessor; import com.limegroup.gnutella.xml.LimeXMLDocument; import com.limegroup.gnutella.xml.LimeXMLSchemaRepository; /** * This class tests the Response class. */ public final class ResponseTest extends com.limegroup.gnutella.util.BaseTestCase { /** * Constructs a new test instance for responses. */ public ResponseTest(String name) { super(name); } public static Test suite() { return buildTestSuite(ResponseTest.class); } /** * Runs this test individually. */ public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } /** * Modified version of the unit test that was formerly embedded in * the Response class. */ public void testLegacyResponseUnitTest() throws Exception { Response r = new Response(3,4096,"A.mp3"); assertEquals("A.mp3", r.getName()); assertNull(r.getDocument()); // //Response r2 = new Response("",999,4,"blah.txt"); //assertEquals("bad meta", null, r2.getMetaBytes()); //assertEquals("Meta size not right", 0, r2.getMetaBytesSize()); //Assert.that(r2.getMetaBytes()==null,"bad meta"); //Assert.that(r2.getMetaBytesSize() == 0,"Meta size not right"); //String md = "Hello"; //Response r3 = new Response(md,999,4,"king.txt"); //assertEquals("bad meta", null, r3.getMetaBytes()); //assertEquals("Meta size not right", 0, r3.getMetaBytesSize()); //Assert.that(r3.getMetaBytes()==null,"bad meta"); //Assert.that(r3.getMetaBytesSize() == 0,"Meta size not right"); //The three formats we support /* String[] meta = {"a kbps 44.1 kHz b","akbps 44.1 kHz b", "b akbps 44.1kHz" }; for(int i=0;i<meta.length;i++){ Response r4 = new Response(meta[i],999+i,4,"abc.txt"); LimeXMLDocument d=null; String xml = r4.getMetadata(); try{ d = new LimeXMLDocument(xml); }catch (Exception e){ assertTrue("XML not created well from between nulls", false); //Assert.that(false,"XML not created well from between nulls"); } String br = d.getValue("audios__audio__bitrate__"); assertEquals("a", br); //Assert.that(br.equals("a")); String len = d.getValue("audios__audio__seconds__"); assertEquals("b", len); //Assert.that(len.equals("b")); } */ //Tests for checking new LimeXMLDocument code added. String xml1 = "<?xml version=\"1.0\"?><audios xsi:noNamespaceSchemaLocation=\"http://www.limewire.com/schemas/audio.xsd\"><audio genre=\"Speech\" bitrate=\"192\"></audio></audios>"; String xml2 = "<?xml version=\"1.0\"?><audios xsi:noNamespaceSchemaLocation=\"http://www.limewire.com/schemas/audio.xsd\"><audio genre=\"Speech\" bitrate=\"150\"></audio></audios>"; //create documents. LimeXMLDocument d1 = null; LimeXMLDocument d2 = null; d1 = new LimeXMLDocument(xml1); d2 = new LimeXMLDocument(xml2); Response ra = new Response(12,231,"def1.txt",d1); Response rb = new Response(13,232,"def2.txt",d2); assertEquals("problem with doc constructor", d1, ra.getDocument()); assertEquals("problem with doc constructor", d2, rb.getDocument()); } /** * Tests a simple response can be created from stream (no extensions) */ public void testSimpleCreateFromStream() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteOrder.int2leb(257, baos); ByteOrder.int2leb(1029, baos); byte[] name = new byte[] { 's', 'a', 'm', 0 }; baos.write(name); // write out closing null. baos.write((byte)0); // add on 16 bytes so it thinks a GUID is there. baos.write( new byte[16] ); baos.flush(); byte[] output = baos.toByteArray(); ByteArrayInputStream in = new ByteArrayInputStream(output); Response r = Response.createFromStream(in); assertEquals("wrong index", 257, r.getIndex()); assertEquals("wrong size", 1029, r.getSize()); assertEquals("wrong name", "sam", r.getName()); for(int i = 0; i < 16; i++) // read the blank GUID stuff we added assertEquals(0, in.read()); assertEquals("leftover input", -1, in.read()); } /** * Tests multiple responses can be stacked together (no extensions) */ public void testMultipleSimpleResponses() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); // RESPONSE 1. ByteOrder.int2leb(257, baos); ByteOrder.int2leb(1029, baos); byte[] name = new byte[] { 's', 'a', 'm', 0 }; baos.write(name); // write out closing null. baos.write((byte)0); // RESPONSE 2. ByteOrder.int2leb(2181, baos); ByteOrder.int2leb(1981, baos); name = new byte[] { 's', '.', 'a', '.', 'b', 0 }; baos.write(name); // write out closing null. baos.write((byte)0); // add on 16 bytes so it thinks a GUID is there. baos.write( new byte[16] ); baos.flush(); byte[] output = baos.toByteArray(); ByteArrayInputStream in = new ByteArrayInputStream(output); Response r = Response.createFromStream(in); assertEquals("wrong index", 257, r.getIndex()); assertEquals("wrong size", 1029, r.getSize()); assertEquals("wrong name", "sam", r.getName()); r = Response.createFromStream(in); assertEquals("wrong index", 2181, r.getIndex()); assertEquals("wrong size", 1981, r.getSize()); assertEquals("wrong name", "s.a.b", r.getName()); for(int i = 0; i < 16; i++) // read the blank GUID stuff we added assertEquals(0, in.read()); assertEquals("leftover input", -1, in.read()); } /** * Tests a response can have some data between the nulls (garbage) */ public void testGarbageExtension() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteOrder.int2leb(257, baos); ByteOrder.int2leb(1029, baos); byte[] name = new byte[] { 's', 'a', 'm', 0 }; baos.write(name); baos.write("this is a garbage extension".getBytes()); // write out closing null. baos.write((byte)0); byte[] output = baos.toByteArray(); ByteArrayInputStream in = new ByteArrayInputStream(output); Response r = Response.createFromStream(in); assertEquals("wrong index", 257, r.getIndex()); assertEquals("wrong size", 1029, r.getSize()); assertEquals("wrong name", "sam", r.getName()); assertEquals("wrong extension", "this is a garbage extension", new String(r.getExtBytes())); assertEquals("leftover input", -1, in.read()); } /** * Tests multiple extensions (garbage) can be added with the delimiter. */ public void testMultipleGarbageExtensions() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteOrder.int2leb(257, baos); ByteOrder.int2leb(1029, baos); byte[] name = new byte[] { 's', 'a', 'm', 0 }; baos.write(name); baos.write("this is a garbage extension".getBytes()); baos.write((byte)0x1c); baos.write("this is another garbage extension".getBytes()); // write out closing null. baos.write((byte)0); byte[] output = baos.toByteArray(); ByteArrayInputStream in = new ByteArrayInputStream(output); Response r = Response.createFromStream(in); assertEquals("wrong index", 257, r.getIndex()); assertEquals("wrong size", 1029, r.getSize()); assertEquals("wrong name", "sam", r.getName()); assertEquals("wrong extension", "this is a garbage extension\u001cthis is another garbage extension", new String(r.getExtBytes())); assertEquals("leftover input", -1, in.read()); } /** * Tests that a URN can be read from a response. */ public void testHUGEUrn() throws Exception { final String sha1 = "urn:sha1:PLSTHIPQGSSZTS5FJUPAKUZWUGYQYPFB"; ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteOrder.int2leb(257, baos); ByteOrder.int2leb(1029, baos); byte[] name = new byte[] { 's', 'a', 'm', 0 }; baos.write(name); // add the sha1 baos.write(sha1.getBytes()); // write out closing null. baos.write((byte)0); byte[] output = baos.toByteArray(); ByteArrayInputStream in = new ByteArrayInputStream(output); Response r = Response.createFromStream(in); assertEquals("wrong index", 257, r.getIndex()); assertEquals("wrong size", 1029, r.getSize()); assertEquals("wrong name", "sam", r.getName()); assertEquals("wrong extension", sha1, new String(r.getExtBytes())); assertEquals("leftover input", -1, in.read()); Set urns = new HashSet(); URN urn = URN.createSHA1UrnFromUriRes("/uri-res/N2R?" + sha1); urns.add(urn); assertEquals("wrong urns", urns, r.getUrns()); } /** * Tests that HUGE can work even if seperated with chunk extensions. */ public void testHUGEUrnWithOtherExtensions() throws Exception { final String sha1 = "urn:sha1:PLSTHIPQGSSZTS5FJUPAKUZWUGYQYPFB"; ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteOrder.int2leb(257, baos); ByteOrder.int2leb(1029, baos); byte[] name = new byte[] { 's', 'a', 'm', 0 }; baos.write(name); // add the sha1 baos.write("junkie junk".getBytes()); baos.write((byte)0x1c); baos.write(sha1.getBytes()); baos.write((byte)0x1c); baos.write("jankie jank".getBytes()); // write out closing null. baos.write((byte)0); byte[] output = baos.toByteArray(); ByteArrayInputStream in = new ByteArrayInputStream(output); Response r = Response.createFromStream(in); assertEquals("wrong index", 257, r.getIndex()); assertEquals("wrong size", 1029, r.getSize()); assertEquals("wrong name", "sam", r.getName()); assertEquals("wrong extension", "junkie junk\u001c"+sha1+"\u001cjankie jank", new String(r.getExtBytes())); assertEquals("leftover input", -1, in.read()); Set urns = new HashSet(); URN urn = URN.createSHA1UrnFromUriRes("/uri-res/N2R?" + sha1); urns.add(urn); assertEquals("wrong urns", urns, r.getUrns()); } /** * Tests that we understand bearshare1-style metadata */ public void testBearShare1Extensions() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteOrder.int2leb(257, baos); ByteOrder.int2leb(1029, baos); byte[] name = new byte[] { 's', 'a', 'm', 0 }; baos.write(name); baos.write("192 kbps 160".getBytes()); // write out closing null. baos.write((byte)0); byte[] output = baos.toByteArray(); ByteArrayInputStream in = new ByteArrayInputStream(output); Response r = Response.createFromStream(in); assertEquals("wrong index", 257, r.getIndex()); assertEquals("wrong size", 1029, r.getSize()); assertEquals("wrong name", "sam", r.getName()); assertEquals("wrong extension", "192 kbps 160", new String(r.getExtBytes())); assertEquals("leftover input", -1, in.read()); LimeXMLDocument doc = r.getDocument(); assertNotNull("should have document", doc); assertEquals("wrong name", "sam", doc.getValue("audios__audio__title__")); assertEquals("wrong bitrate", "192", doc.getValue("audios__audio__bitrate__")); assertEquals("wrong length", "160", doc.getValue("audios__audio__seconds__")); } /** * Tests that we understand bearshare2-style metadata */ public void testBearShare2Extensions() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteOrder.int2leb(257, baos); ByteOrder.int2leb(1029, baos); byte[] name = new byte[] { 's', 'a', 'm', 'm', 'y', 0 }; baos.write(name); baos.write("256kbps something 3528".getBytes()); // write out closing null. baos.write((byte)0); byte[] output = baos.toByteArray(); ByteArrayInputStream in = new ByteArrayInputStream(output); Response r = Response.createFromStream(in); assertEquals("wrong index", 257, r.getIndex()); assertEquals("wrong size", 1029, r.getSize()); assertEquals("wrong name", "sammy", r.getName()); assertEquals("wrong extension", "256kbps something 3528", new String(r.getExtBytes())); assertEquals("leftover input", -1, in.read()); LimeXMLDocument doc = r.getDocument(); assertNotNull("should have document", doc); assertEquals("wrong name", "sammy", doc.getValue("audios__audio__title__")); assertEquals("wrong bitrate", "256", doc.getValue("audios__audio__bitrate__")); assertEquals("wrong length", "3528", doc.getValue("audios__audio__seconds__")); } /** * Tests that GGEP'd ALT extensions are read correctly. */ public void testGGEPAltExtension() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteOrder.int2leb(257, baos); ByteOrder.int2leb(1029, baos); byte[] name = new byte[] { 's', 'a', 'm', 0 }; baos.write(name); GGEP info = new GGEP(); // locations: 1.2.3.4:1, 4.3.2.1:2 byte[] alts = { 1, 2, 3, 4, 1, 0, 4, 3, 2, 1, 2, 0 }; info.put(GGEP.GGEP_HEADER_ALTS, alts); info.write(baos); // write out closing null. baos.write((byte)0); byte[] output = baos.toByteArray(); ByteArrayInputStream in = new ByteArrayInputStream(output); Response r = Response.createFromStream(in); assertEquals("wrong index", 257, r.getIndex()); assertEquals("wrong size", 1029, r.getSize()); assertEquals("wrong name", "sam", r.getName()); // too annoying to check extension was correct. assertEquals("leftover input", -1, in.read()); assertEquals("wrong number of locations", 2, r.getLocations().size()); Set endpoints = new HashSet(); endpoints.add( new Endpoint("1.2.3.4", 1) ); endpoints.add( new Endpoint("4.3.2.1", 2) ); assertEquals("wrong alts", endpoints, r.getLocations()); assertEquals("should have no time", -1, r.getCreateTime()); } /** * Tests that the GGEP'd CT extension is read correctly. */ public void testGGEPCreateTimeExtension() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteOrder.int2leb(257, baos); ByteOrder.int2leb(1029, baos); byte[] name = new byte[] { 's', 'a', 'm', 0 }; baos.write(name); GGEP info = new GGEP(); long time = System.currentTimeMillis(); info.put(GGEP.GGEP_HEADER_CREATE_TIME, time / 1000); info.write(baos); // write out closing null. baos.write((byte)0); byte[] output = baos.toByteArray(); ByteArrayInputStream in = new ByteArrayInputStream(output); Response r = Response.createFromStream(in); assertEquals("wrong index", 257, r.getIndex()); assertEquals("wrong size", 1029, r.getSize()); assertEquals("wrong name", "sam", r.getName()); // too annoying to check extension was correct. assertEquals("leftover input", -1, in.read()); time = time / 1000 * 1000; // we lose precision when sending. assertEquals("wrong time", time, r.getCreateTime()); assertEquals("should have no locs", new HashSet(), r.getLocations()); } /** * Test GGEP with multiple extensions. */ public void testGGEPMultipleExtensions() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteOrder.int2leb(257, baos); ByteOrder.int2leb(1029, baos); byte[] name = new byte[] { 's', 'a', 'm', 0 }; baos.write(name); GGEP info = new GGEP(); long time = System.currentTimeMillis(); info.put(GGEP.GGEP_HEADER_CREATE_TIME, time / 1000); // locations: 1.2.3.4:1, 4.3.2.1:2 byte[] alts = { 1, 2, 3, 4, 1, 0, 4, 3, 2, 1, 2, 0 }; info.put(GGEP.GGEP_HEADER_ALTS, alts); info.write(baos); // write out closing null. baos.write((byte)0); byte[] output = baos.toByteArray(); ByteArrayInputStream in = new ByteArrayInputStream(output); Response r = Response.createFromStream(in); assertEquals("wrong index", 257, r.getIndex()); assertEquals("wrong size", 1029, r.getSize()); assertEquals("wrong name", "sam", r.getName()); // too annoying to check extension was correct. assertEquals("leftover input", -1, in.read()); time = time / 1000 * 1000; // we lose precision when sending. assertEquals("wrong time", time, r.getCreateTime()); assertEquals("wrong number of locations", 2, r.getLocations().size()); Set endpoints = new HashSet(); endpoints.add( new Endpoint("1.2.3.4", 1) ); endpoints.add( new Endpoint("4.3.2.1", 2) ); assertEquals("wrong alts", endpoints, r.getLocations()); } /** * Tests that GGEP can be both before & after other extensions. */ public void testGGEPWithOtherExtensions() throws Exception { final String sha1 = "urn:sha1:PLSTHIPQGSSZTS5FJUPAKUZWUGYQYPFB"; ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteOrder.int2leb(257, baos); ByteOrder.int2leb(1029, baos); byte[] name = new byte[] { 's', 'a', 'm', 0 }; baos.write(name); baos.write(sha1.getBytes()); baos.write((byte)0x1c); GGEP info = new GGEP(); // locations: 1.2.3.4:1, 4.3.2.1:2 byte[] alts = { 1, 2, 3, 4, 1, 0, 4, 3, 2, 1, 2, 0 }; info.put(GGEP.GGEP_HEADER_ALTS, alts); info.write(baos); baos.write((byte)0x1c); baos.write("192 kbps 160".getBytes()); // write out closing null. baos.write((byte)0); byte[] output = baos.toByteArray(); ByteArrayInputStream in = new ByteArrayInputStream(output); Response r = Response.createFromStream(in); assertEquals("wrong index", 257, r.getIndex()); assertEquals("wrong size", 1029, r.getSize()); assertEquals("wrong name", "sam", r.getName()); // too annoying to check extension was correct. assertEquals("leftover input", -1, in.read()); assertEquals("wrong number of locations", 2, r.getLocations().size()); Set endpoints = new HashSet(); endpoints.add( new Endpoint("1.2.3.4", 1) ); endpoints.add( new Endpoint("4.3.2.1", 2) ); assertEquals("wrong alts", endpoints, r.getLocations()); Set urns = new HashSet(); URN urn = URN.createSHA1UrnFromUriRes("/uri-res/N2R?" + sha1); urns.add(urn); assertEquals("wrong urns", urns, r.getUrns()); LimeXMLDocument doc = r.getDocument(); assertNotNull("should have document", doc); assertEquals("wrong name", "sam", doc.getValue("audios__audio__title__")); assertEquals("wrong bitrate", "192", doc.getValue("audios__audio__bitrate__")); assertEquals("wrong length", "160", doc.getValue("audios__audio__seconds__")); } /** * Tests the GGEPUtil.addGGEP method to correctly write * the correct number of alts out, in the correct format. */ public void testOnly10AltsAreWritten() throws Exception { final String sha1 = "urn:sha1:PLSTHIPQGSSZTS5FJUPAKUZWUGYQYPFB"; ByteArrayOutputStream baos = new ByteArrayOutputStream(); URN urn = URN.createSHA1UrnFromUriRes("/uri-res/N2R?" + sha1); // First create a bunch of alts. AlternateLocationCollection alc = AlternateLocationCollection.create(urn); for(int i = 0; i < 20; i++) { AlternateLocation al = AlternateLocation.create( "http://1.2.3." + i + ":1/uri-res/N2R?" + sha1); alc.add(al); } // Then get them as endpoints (should remove the extra alts). Set endpoints = getAsEndpoints(alc); assertEquals("didn't filter out extras", 10, endpoints.size()); // Add them to the output stream as a GGEP block. Response.GGEPContainer gc = new Response.GGEPContainer(endpoints, -1); addGGEP(baos, gc); // See if we can correctly read the GGEP block. baos.flush(); byte[] output = baos.toByteArray(); GGEP alt = new GGEP(output, 0, null); Set headers = alt.getHeaders(); assertEquals("wrong size", 1, headers.size()); assertEquals("wrong header", "ALT", headers.iterator().next()); byte[] data = alt.getBytes("ALT"); assertEquals("wrong data length", 60, data.length); for(int i = 0; i < data.length; i+=6) { assertEquals(1, data[i]); assertEquals(2, data[i+1]); assertEquals(3, data[i+2]); // data[i+3] is the one that changed, and because // iterators may return values in any order, // it doesn't matter what it is, so long as everything // else is correct. assertEquals(1, data[i+4]); assertEquals(0, data[i+5]); } } /** * Tests the GGEPUtil.getGGEP method. */ public void testGGEPUtilGetGGEP() throws Exception { GGEP ggep = new GGEP(); Response.GGEPContainer container; long ctime; Set locs; byte[] data; data = new byte[20]; // not % 6. ggep.put("ALT", data); locs = getGGEP(ggep).locations; assertNotNull("should never be null", locs); assertEquals("shouldn't have locs", 0, locs.size()); data = new byte[18]; // multiple of 6, but all blank (invalid) ggep.put("ALT", data); locs = getGGEP(ggep).locations; assertNotNull("should never be null", locs); assertEquals("shouldn't have locs", 0, locs.size()); // multiple of 6, but same byte[] d1 = {1, 2, 3, 4, 1, 0, 1, 2, 3, 4, 1, 0}; ggep.put("ALT", d1); locs = getGGEP(ggep).locations; assertNotNull("should never be null", locs); assertEquals("wrong number of locs", 1, locs.size()); assertEquals("wrong endpoint", new Endpoint("1.2.3.4:1"), locs.iterator().next()); // multiple of 6, diff byte[] d2 = {1, 2, 3, 4, 1, 0, 1, 2, 3, 4, 2, 0}; ggep.put("ALT", d2); locs = getGGEP(ggep).locations; assertNotNull("should never be null", locs); assertEquals("wrong number of locs", 2, locs.size()); Set eps = new HashSet(); eps.add( new Endpoint("1.2.3.4:1") ); eps.add( new Endpoint("1.2.3.4:2") ); assertEquals("wrong endpoints", eps, locs); // ctime. ggep = new GGEP(); ggep.put("CT", 5341L); ctime = getGGEP(ggep).createTime; assertEquals(5341000, ctime); // alt & ctime. ggep.put("CT", 1243L); ggep.put("ALT", d2); container = getGGEP(ggep); assertNotNull(container); assertNotNull(container.locations); assertEquals(2, container.locations.size()); assertEquals(eps, container.locations); assertEquals(1243000, container.createTime); // invalid alt, valid ctime ggep.put("ALT", new byte[0]); ggep.put("CT", 3214); container = getGGEP(ggep); assertEquals(0, container.locations.size()); assertEquals(3214000, container.createTime); // invalid ctime, valid alt ggep.put("ALT", d2); // use 9 bytes (1 byte over the max for a long) ggep.put("CT", new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0 } ); container = getGGEP(ggep); assertEquals(2, container.locations.size()); assertEquals(eps, container.locations); assertEquals(-1, container.createTime); } private Set getAsEndpoints(AlternateLocationCollection col) throws Exception { return (Set)PrivilegedAccessor.invokeMethod(Response.class, "getAsEndpoints", new Object[] { col } ); } private void addGGEP(OutputStream os, Response.GGEPContainer gc) throws Exception { Class c = PrivilegedAccessor.getClass(Response.class, "GGEPUtil"); PrivilegedAccessor.invokeMethod(c, "addGGEP", new Object[] { os, gc }, new Class[] { OutputStream.class, Response.GGEPContainer.class } ); } private Response.GGEPContainer getGGEP(GGEP info) throws Exception { Class c = PrivilegedAccessor.getClass(Response.class, "GGEPUtil"); return (Response.GGEPContainer)PrivilegedAccessor.invokeMethod( c, "getGGEP", new Object[] { info } ); } }