package com.limegroup.gnutella;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import junit.framework.Test;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.limewire.collection.IntervalSet;
import org.limewire.collection.Range;
import org.limewire.core.settings.MessageSettings;
import org.limewire.io.Address;
import org.limewire.io.Connectable;
import org.limewire.io.GGEP;
import org.limewire.io.GUID;
import org.limewire.io.IpPort;
import org.limewire.io.IpPortImpl;
import org.limewire.io.IpPortSet;
import org.limewire.util.ByteUtils;
import org.limewire.util.PrivilegedAccessor;
import org.limewire.util.StringUtils;
import com.google.inject.Injector;
import com.limegroup.gnutella.altlocs.AlternateLocation;
import com.limegroup.gnutella.altlocs.AlternateLocationCollection;
import com.limegroup.gnutella.altlocs.AlternateLocationFactory;
import com.limegroup.gnutella.downloader.RemoteFileDescFactory;
import com.limegroup.gnutella.downloader.VerifyingFile;
import com.limegroup.gnutella.downloader.VerifyingFileFactory;
import com.limegroup.gnutella.filters.LocalIPFilter;
import com.limegroup.gnutella.filters.XMLDocFilterTest;
import com.limegroup.gnutella.filters.IPFilter.IPFilterCallback;
import com.limegroup.gnutella.helpers.UrnHelper;
import com.limegroup.gnutella.library.FileDesc;
import com.limegroup.gnutella.library.FileDescFactory;
import com.limegroup.gnutella.library.IncompleteFileDesc;
import com.limegroup.gnutella.messages.BadPacketException;
import com.limegroup.gnutella.messages.GGEPKeys;
import com.limegroup.gnutella.messages.QueryReply;
import com.limegroup.gnutella.messages.QueryReplyFactory;
import com.limegroup.gnutella.xml.LimeXMLDocument;
import com.limegroup.gnutella.xml.LimeXMLDocumentFactory;
import com.limegroup.gnutella.xml.LimeXMLDocumentHelper;
/**
* This class tests the Response class.
*/
@SuppressWarnings("unchecked")
// TODO should be renamed to response factory test maybe
public final class ResponseTest extends com.limegroup.gnutella.util.LimeTestCase {
private QueryReplyFactory queryReplyFactory;
private ResponseFactoryImpl responseFactoryImpl;
private LimeXMLDocumentFactory limeXMLDocumentFactory;
private AlternateLocationFactory alternateLocationFactory;
private LimeXMLDocumentHelper limeXMLDocumentHelper;
private Injector injector;
private FileDescFactory fileDescFactory;
private Mockery context;
private RemoteFileDescFactory remoteFileDescFactory;
private PushEndpointFactory pushEndpointFactory;
/**
* 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());
}
@Override
protected void setUp() throws Exception {
context = new Mockery();
injector = LimeTestUtils.createInjector();
queryReplyFactory = injector.getInstance(QueryReplyFactory.class);
responseFactoryImpl = (ResponseFactoryImpl) injector.getInstance(ResponseFactory.class);
limeXMLDocumentFactory = injector.getInstance(LimeXMLDocumentFactory.class);
limeXMLDocumentHelper = injector.getInstance(LimeXMLDocumentHelper.class);
alternateLocationFactory = injector.getInstance(AlternateLocationFactory.class);
fileDescFactory = injector.getInstance(FileDescFactory.class);
remoteFileDescFactory = injector.getInstance(RemoteFileDescFactory.class);
pushEndpointFactory = injector.getInstance(PushEndpointFactory.class);
final CountDownLatch latch = new CountDownLatch(1);
injector.getInstance(LocalIPFilter.class).refreshHosts(new IPFilterCallback() {
public void ipFiltersLoaded() {
latch.countDown();
}
});
assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
}
/**
* Modified version of the unit test that was formerly embedded in
* the Response class.
*/
public void testLegacyResponseUnitTest() throws Exception {
Response r = responseFactoryImpl.createResponse(3, 4096, "A.mp3", UrnHelper.SHA1);
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.
LimeXMLDocumentFactory factory = limeXMLDocumentFactory;
LimeXMLDocument d1 = null;
LimeXMLDocument d2 = null;
d1 = factory.createLimeXMLDocument(xml1);
d2 = factory.createLimeXMLDocument(xml2);
Response ra = responseFactoryImpl.createResponse(12, 231, "def1.txt", d1, UrnHelper.SHA1);
Response rb = responseFactoryImpl.createResponse(13, 232, "def2.txt", d2, UrnHelper.SHA1);
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();
ByteUtils.int2leb(257, baos);
ByteUtils.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 = responseFactoryImpl.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.
ByteUtils.int2leb(257, baos);
ByteUtils.int2leb(1029, baos);
byte[] name = new byte[] { 's', 'a', 'm', 0 };
baos.write(name);
// write out closing null.
baos.write((byte)0);
// RESPONSE 2.
ByteUtils.int2leb(2181, baos);
ByteUtils.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 = responseFactoryImpl.createFromStream(in);
assertEquals("wrong index", 257, r.getIndex());
assertEquals("wrong size", 1029, r.getSize());
assertEquals("wrong name", "sam", r.getName());
r = responseFactoryImpl.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();
ByteUtils.int2leb(257, baos);
ByteUtils.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 = responseFactoryImpl.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();
ByteUtils.int2leb(257, baos);
ByteUtils.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 = responseFactoryImpl.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();
ByteUtils.int2leb(257, baos);
ByteUtils.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 = responseFactoryImpl.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();
ByteUtils.int2leb(257, baos);
ByteUtils.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 = responseFactoryImpl.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();
ByteUtils.int2leb(257, baos);
ByteUtils.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 = responseFactoryImpl.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();
ByteUtils.int2leb(257, baos);
ByteUtils.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 = responseFactoryImpl.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();
ByteUtils.int2leb(257, baos);
ByteUtils.int2leb(1029, baos);
byte[] name = new byte[] { 's', 'a', 'm', 0 };
baos.write(name);
GGEP info = new GGEP(true);
// 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(GGEPKeys.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 = responseFactoryImpl.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 IpPortSet();
endpoints.add( new IpPortImpl("1.2.3.4", 1) );
endpoints.add( new IpPortImpl("4.3.2.1", 2) );
assertEquals("wrong alts", endpoints, r.getLocations());
assertEquals("should have no time", -1, r.getCreateTime());
}
/** Tests TLS is read in addition. */
public void testReadGGEPAltWithTLSExtension() throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ByteUtils.int2leb(257, baos);
ByteUtils.int2leb(1029, baos);
byte[] name = new byte[] { 's', 'a', 'm', 0 };
baos.write(name);
GGEP info = new GGEP(true);
// locations: 1.2.3.4:1, 4.3.2.1:2, 2.3.4.5:6, 5.4.3.2:7, 2.3.3.2:8
byte[] alts = { 1, 2, 3, 4, 1, 0, 4, 3, 2, 1, 2, 0, 2, 3, 4, 5, 6, 0, 5, 4, 3, 2, 7, 0, 2, 3, 3, 2, 8, 0 };
info.put(GGEPKeys.GGEP_HEADER_ALTS, alts);
byte[] tlsIdx = { (byte)0xC8 }; // 11001
info.put(GGEPKeys.GGEP_HEADER_ALTS_TLS, tlsIdx );
info.write(baos);
// write out closing null.
baos.write((byte)0);
byte[] output = baos.toByteArray();
ByteArrayInputStream in = new ByteArrayInputStream(output);
Response r = responseFactoryImpl.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", 5, r.getLocations().size());
Set tls = new IpPortSet();
Set nonTLS = new IpPortSet();
for(IpPort ipp : r.getLocations()) {
if(ipp instanceof Connectable && ((Connectable)ipp).isTLSCapable())
tls.add(ipp);
else
nonTLS.add(ipp);
}
// TLS should be 3, nonTLS == 2
assertEquals(3, tls.size());
assertEquals(2, nonTLS.size());
Set endpoints = new IpPortSet();
endpoints.add( new IpPortImpl("1.2.3.4", 1) );
endpoints.add( new IpPortImpl("4.3.2.1", 2) );
endpoints.add( new IpPortImpl("2.3.3.2", 8) );
assertEquals(endpoints, tls);
endpoints.clear();
endpoints.add( new IpPortImpl("2.3.4.5", 6) );
endpoints.add( new IpPortImpl("5.4.3.2", 7) );
assertEquals(endpoints, nonTLS);
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();
ByteUtils.int2leb(257, baos);
ByteUtils.int2leb(1029, baos);
byte[] name = new byte[] { 's', 'a', 'm', 0 };
baos.write(name);
GGEP info = new GGEP(true);
long time = System.currentTimeMillis();
info.put(GGEPKeys.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 = responseFactoryImpl.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();
ByteUtils.int2leb(257, baos);
ByteUtils.int2leb(1029, baos);
byte[] name = new byte[] { 's', 'a', 'm', 0 };
baos.write(name);
GGEP info = new GGEP(true);
long time = System.currentTimeMillis();
info.put(GGEPKeys.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(GGEPKeys.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 = responseFactoryImpl.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 IpPortSet();
endpoints.add( new IpPortImpl("1.2.3.4", 1) );
endpoints.add( new IpPortImpl("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();
ByteUtils.int2leb(257, baos);
ByteUtils.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(true);
// 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(GGEPKeys.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 = responseFactoryImpl.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 IpPortSet();
endpoints.add( new IpPortImpl("1.2.3.4", 1) );
endpoints.add( new IpPortImpl("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 = alternateLocationFactory.create("1.2.3." + i + ":1", urn);
alc.add(al);
}
// Then get them as endpoints (should remove the extra alts).
Set endpoints = getAsIpPorts(alc);
assertEquals("didn't filter out extras", 10, endpoints.size());
// Add them to the output stream as a GGEP block.
ResponseFactoryImpl.GGEPContainer gc = new ResponseFactoryImpl.GGEPContainer(endpoints, -1, 0, null, false, null);
addGGEP(baos, gc, 0);
// 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]);
}
}
public void testAltsAndTLSAreWritten() 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 < 10; i++) {
AlternateLocation al = alternateLocationFactory.create("1.2.3." + i + ":1", urn, i % 3 == 0);
alc.add(al);
}
// Then get them as endpoints (should remove the extra alts).
Set endpoints = getAsIpPorts(alc);
assertEquals("didn't filter out extras", 10, endpoints.size());
// Add them to the output stream as a GGEP block.
ResponseFactoryImpl.GGEPContainer gc = new ResponseFactoryImpl.GGEPContainer(endpoints, -1, 0, null, false, null);
addGGEP(baos, gc, 0);
// 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", 2, headers.size());
assertTrue("no alt!", headers.contains("ALT"));
assertTrue("no tls!", headers.contains("ALT_TLS"));
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]);
assertEquals((int)Math.floor(i / 6), data[i+3]);
assertEquals(1, data[i+4]);
assertEquals(0, data[i+5]);
}
data = alt.getBytes("ALT_TLS");
assertEquals("wrong tls length", 2, data.length);
assertEquals(new byte[] { (byte)0x92, (byte)0x40 }, data );
}
/**
* Tests the GGEPUtil.getGGEP method.
*/
public void testGGEPUtilGetGGEP() throws Exception {
GGEP ggep = new GGEP(true);
ResponseFactoryImpl.GGEPContainer container;
long ctime;
Set locs;
byte[] data;
data = new byte[20]; // not % 6.
ggep.put("ALT", data);
locs = responseFactoryImpl.getGGEP(ggep, 0).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 = responseFactoryImpl.getGGEP(ggep, 0).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 = responseFactoryImpl.getGGEP(ggep, 0).locations;
assertNotNull("should never be null", locs);
assertEquals("wrong number of locs", 1, locs.size());
assertEquals("wrong endpoint", 0,
IpPort.COMPARATOR.compare(new IpPortImpl("1.2.3.4:1"), (IpPort)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 = responseFactoryImpl.getGGEP(ggep, 0).locations;
assertNotNull("should never be null", locs);
assertEquals("wrong number of locs", 2, locs.size());
Set eps = new IpPortSet();
eps.add( new IpPortImpl("1.2.3.4:1") );
eps.add( new IpPortImpl("1.2.3.4:2") );
assertEquals("wrong endpoints", eps, locs);
// ctime.
ggep = new GGEP(true);
ggep.put("CT", 5341L);
ctime = responseFactoryImpl.getGGEP(ggep, 0).createTime;
assertEquals(5341000, ctime);
// alt & ctime.
ggep.put("CT", 1243L);
ggep.put("ALT", d2);
container = responseFactoryImpl.getGGEP(ggep, 0);
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 = responseFactoryImpl.getGGEP(ggep, 0);
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 = responseFactoryImpl.getGGEP(ggep, 0);
assertEquals(2, container.locations.size());
assertEquals(eps, container.locations);
assertEquals(-1, container.createTime);
}
public void testHashCode() {
Response r1 = responseFactoryImpl.createResponse(0, 0, "name", UrnHelper.SHA1);
Response r2 = responseFactoryImpl.createResponse(0, 1, "name", UrnHelper.SHA1);
assertNotEquals(r1.hashCode(), r2.hashCode());
Response r3 = responseFactoryImpl.createResponse(1, 0, "name", UrnHelper.SHA1);
assertNotEquals(r1.hashCode(), r3.hashCode());
assertNotEquals(r2.hashCode(), r3.hashCode());
assertEquals(r1.hashCode(), responseFactoryImpl.createResponse(0, 0, "name", UrnHelper.SHA1).hashCode());
// max int values
r1 = responseFactoryImpl
.createResponse(Integer.MAX_VALUE, Integer.MAX_VALUE, "name", UrnHelper.SHA1);
r2 = responseFactoryImpl.createResponse(0, Integer.MAX_VALUE, "name", UrnHelper.SHA1);
assertNotEquals(r1.hashCode(), r2.hashCode());
}
public void testIllegalFilenamesInInputStream() throws Exception {
// illegal filename
Response resp = responseFactoryImpl.createResponse(1, 2, "a;lksdflkfj../", UrnHelper.SHA1);
assertResponseParsingFails(resp);
assertResponseParsingFails(responseFactoryImpl.createResponse(1, 4545, "s;lkdf\n\n\n", UrnHelper.SHA1));
assertResponseParsingFails(responseFactoryImpl.createResponse(1, 4545, "../../index.html HTTP/1.0\r\n\r\nfoobar.mp3", UrnHelper.SHA1));
assertResponseParsingFails(responseFactoryImpl.createResponse(4545, 3454, "", UrnHelper.SHA1));
assertResponseParsingFails(responseFactoryImpl.createResponse(1454, 3245, "dlksdf\r", UrnHelper.SHA1));
}
public void testLargeFiles() throws Exception {
Response resp = responseFactoryImpl.createResponse(1, Constants.MAX_FILE_SIZE, "asdf", UrnHelper.SHA1);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
resp.writeToStream(baos);
byte [] data = baos.toByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream(data);
Response read = responseFactoryImpl.createFromStream(bais);
assertEquals(Constants.MAX_FILE_SIZE, read.getSize());
// also check the data manually
// 1. the legacy size field should have all bits set
for (int i = 4; i < 8; i++)
assertEquals(-1, data[i]);
// find where the ggep starts
int firstNull = 8;
// skip to first 0x0 to find extension bytes
while(data[firstNull++] != 0x0);
// inside extension bytes skip over sha1, to ggep
while(data[firstNull++] != 0x1c);
GGEP g = new GGEP(data, firstNull);
assertEquals(Constants.MAX_FILE_SIZE, g.getLong(GGEPKeys.GGEP_HEADER_LARGE_FILE));
// if the file is too large, we do not construct
try {
resp = responseFactoryImpl.createResponse(1, Constants.MAX_FILE_SIZE + 1, "asdf", UrnHelper.SHA1);
fail("constructed too large file");
} catch (IllegalArgumentException expected){}
// we don't parse responses from network either
g = new GGEP(true);
g.put(GGEPKeys.GGEP_HEADER_LARGE_FILE,Constants.MAX_FILE_SIZE + 1);
baos = new ByteArrayOutputStream();
baos.write(data,0, firstNull);
g.write(baos);
baos.write(0x0);
bais = new ByteArrayInputStream(baos.toByteArray());
try {
responseFactoryImpl.createFromStream(bais);
fail("read a response with too large file");
} catch (IOException expected){}
}
public void testIntervals() throws Exception {
// response with two urns
UrnSet set = new UrnSet();
set.add(UrnHelper.SHA1);
set.add(UrnHelper.TTROOT);
// a verifying file with some stuff verified
VerifyingFileFactory vfactory = injector.getInstance(VerifyingFileFactory.class);
VerifyingFile vf = vfactory.createVerifyingFile(1024*1024);
Range r = Range.createRange(0,1024 * 101 - 1);
IntervalSet verified = new IntervalSet();
verified.add(r);
PrivilegedAccessor.setValue(vf,"verifiedBlocks",verified);
IncompleteFileDesc ifd =
fileDescFactory.createIncompleteFileDesc(new File("a"),set,11,"a",1024 * 1024, vf);
Response resp = responseFactoryImpl.createResponse(ifd);
assertTrue(resp.getUrns().contains(UrnHelper.SHA1));
assertTrue(resp.getUrns().contains(UrnHelper.TTROOT));
IntervalSet s = resp.getRanges();
assertEquals(1,s.getNumberOfIntervals());
assertEquals(1024 * 101, s.getSize());
assertTrue(s.contains(r));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
resp.writeToStream(baos);
byte [] data = baos.toByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream(data);
Response read = responseFactoryImpl.createFromStream(bais);
s = read.getRanges();
assertEquals(1,s.getNumberOfIntervals());
assertEquals(1024 * 101, s.getSize());
assertTrue(s.contains(r));
assertTrue(read.isVerified());
}
public void testUnverifiedIntervals() throws Exception {
// response with two urns
UrnSet set = new UrnSet();
set.add(UrnHelper.SHA1);
set.add(UrnHelper.TTROOT);
// a verifying file with some stuff verified
VerifyingFileFactory vfactory = injector.getInstance(VerifyingFileFactory.class);
VerifyingFile vf = vfactory.createVerifyingFile(1024*1024);
Range r = Range.createRange(0,1024 * 101 - 1);
IntervalSet partial = new IntervalSet();
partial.add(r);
PrivilegedAccessor.setValue(vf,"partialBlocks",partial);
IncompleteFileDesc ifd =
fileDescFactory.createIncompleteFileDesc(new File("a"),set,11,"a",1024 * 1024, vf);
Response resp = responseFactoryImpl.createResponse(ifd);
assertTrue(resp.getUrns().contains(UrnHelper.SHA1));
assertTrue(resp.getUrns().contains(UrnHelper.TTROOT));
IntervalSet s = resp.getRanges();
assertEquals(1,s.getNumberOfIntervals());
assertEquals(1024 * 101, s.getSize());
assertTrue(s.contains(r));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
resp.writeToStream(baos);
byte [] data = baos.toByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream(data);
Response read = responseFactoryImpl.createFromStream(bais);
s = read.getRanges();
assertEquals(1,s.getNumberOfIntervals());
assertEquals(1024 * 101, s.getSize());
assertTrue(s.contains(r));
assertFalse(read.isVerified());
}
public void testTTROOTInGGEP() throws Exception {
MessageSettings.TTROOT_IN_GGEP.setValue(true);
// response with two urns
UrnSet set = new UrnSet();
set.add(UrnHelper.SHA1);
set.add(UrnHelper.TTROOT);
File f = new File("a") {
@Override
public long length() {
return 1;
}
};
FileDesc fd = fileDescFactory.createFileDesc(f, set, 1);
Response resp = responseFactoryImpl.createResponse(fd);
assertTrue(resp.getUrns().contains(UrnHelper.SHA1));
assertTrue(resp.getUrns().contains(UrnHelper.TTROOT));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
resp.writeToStream(baos);
byte [] data = baos.toByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream(data);
Response read = responseFactoryImpl.createFromStream(bais);
assertTrue(read.getUrns().contains(UrnHelper.SHA1));
assertFalse(read.getUrns().contains(UrnHelper.TTROOT));
// go through the data, make sure its not in HUGE
String s = StringUtils.getASCIIString(data).toLowerCase();
assertFalse(s.toLowerCase().contains("ttroot"));
}
public void testTTROOTInHUGE() throws Exception {
MessageSettings.TTROOT_IN_GGEP.setValue(false);
// response with two urns
UrnSet set = new UrnSet();
set.add(UrnHelper.SHA1);
set.add(UrnHelper.TTROOT);
File f = new File("a") {
@Override
public long length() {
return 1;
}
};
FileDesc fd = fileDescFactory.createFileDesc(f, set, 1);
Response resp = responseFactoryImpl.createResponse(fd);
assertTrue(resp.getUrns().contains(UrnHelper.SHA1));
assertTrue(resp.getUrns().contains(UrnHelper.TTROOT));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
resp.writeToStream(baos);
byte [] data = baos.toByteArray();
ByteArrayInputStream bais = new ByteArrayInputStream(data);
Response read = responseFactoryImpl.createFromStream(bais);
assertTrue(read.getUrns().contains(UrnHelper.SHA1));
assertFalse(read.getUrns().contains(UrnHelper.TTROOT));
// go through the data, make sure its in HUGE
String s = StringUtils.getASCIIString(data);
assertTrue(s.toLowerCase().contains("ttroot"));
}
/**
* Ensures the given address is used when creating the rfd.
*/
public void testUsesGivenAddressForRemoteFileDesc() throws Exception {
Response response = responseFactoryImpl.createResponse(100, 200, "hello", UrnHelper.SHA1);
Address address = context.mock(Address.class);
final QueryReply queryReply = context.mock(QueryReply.class);
context.checking(new Expectations() {{
one(queryReply).getClientGUID();
will(returnValue(GUID.makeGuid()));
one(queryReply).getSpeed();
will(returnValue(5));
one(queryReply).calculateQualityOfService();
will(returnValue(0));
one(queryReply).getSupportsBrowseHost();
will(returnValue(true));
one(queryReply).isReplyToMulticastQuery();
will(returnValue(false));
one(queryReply).getVendor();
will(returnValue("vendor"));
}});
RemoteFileDesc rfd = response.toRemoteFileDesc(queryReply, address, remoteFileDescFactory, pushEndpointFactory);
assertSame(address, rfd.getAddress());
context.assertIsSatisfied();
}
public void testIsMetaFileWithAllLocales() throws Exception {
for (Locale locale : Locale.getAvailableLocales()) {
Locale.setDefault(locale);
Response response = new ResponseImpl(100, 100, "hello world.torrent", "hello world.torrent".getBytes("UTF-8").length,
null, null, null, 5000, null, null, false);
assertTrue("Failed for locale: " + locale, response.isMetaFile());
}
}
private void assertResponseParsingFails(Response r) throws Exception {
QueryReply qr = XMLDocFilterTest.createReply(r, 5555, new byte[] { 127, 0, 0, 1 }, queryReplyFactory, limeXMLDocumentHelper);
try {
qr.getResultsArray();
fail("Expected bad packet exception");
}
catch (BadPacketException bpe) {
}
}
private Set getAsIpPorts(AlternateLocationCollection col)
throws Exception {
return (Set)PrivilegedAccessor.invokeMethod(responseFactoryImpl,
"getAsIpPorts", new Object[] { col } );
}
private void addGGEP(OutputStream os, ResponseFactoryImpl.GGEPContainer gc, long size) throws Exception {
PrivilegedAccessor.invokeMethod(responseFactoryImpl, "addGGEP",
new Object[] { os, gc, size },
new Class[] { OutputStream.class, ResponseFactoryImpl.GGEPContainer.class, long.class } );
}
}