package com.limegroup.gnutella;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import junit.framework.Test;
import org.limewire.core.settings.ConnectionSettings;
import org.limewire.core.settings.UploadSettings;
import org.limewire.gnutella.tests.ActivityCallbackStub;
import org.limewire.gnutella.tests.LimeTestUtils;
import org.limewire.gnutella.tests.NetworkManagerStub;
import org.limewire.io.ConnectableImpl;
import org.limewire.io.GUID;
import org.limewire.security.AddressSecurityToken;
import org.limewire.security.MACCalculatorRepositoryManager;
import org.limewire.util.PrivilegedAccessor;
import org.limewire.util.TestUtils;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Singleton;
import com.limegroup.gnutella.Downloader.DownloadState;
import com.limegroup.gnutella.downloader.RemoteFileDescFactory;
import com.limegroup.gnutella.downloader.TestFile;
import com.limegroup.gnutella.downloader.TestUploader;
import com.limegroup.gnutella.guess.GUESSEndpoint;
import com.limegroup.gnutella.guess.OnDemandUnicaster;
import com.limegroup.gnutella.helpers.UrnHelper;
import com.limegroup.gnutella.messages.Message;
import com.limegroup.gnutella.messages.MessageFactory;
import com.limegroup.gnutella.messages.PingReply;
import com.limegroup.gnutella.messages.PingReplyFactory;
import com.limegroup.gnutella.messages.PingRequest;
import com.limegroup.gnutella.messages.QueryReply;
import com.limegroup.gnutella.messages.QueryReplyFactory;
import com.limegroup.gnutella.messages.QueryRequest;
import com.limegroup.gnutella.messages.QueryRequestFactory;
import com.limegroup.gnutella.messages.Message.Network;
import com.limegroup.gnutella.messages.vendor.MessagesSupportedVendorMessage;
import com.limegroup.gnutella.messages.vendor.ReplyNumberVendorMessage;
import com.limegroup.gnutella.messages.vendor.ReplyNumberVendorMessageFactory;
/**
* Checks whether (multi)leaves avoid forwarding messages to ultrapeers, do
* redirects properly, etc. The test includes a leaf attached to 3
* Ultrapeers.
*/
public class ClientSideOOBRequeryTest extends ClientSideTestCase {
private static final int UPLOADER_PORT = 10000;
private MyCallback callback;
/**
* Ultrapeer 1 UDP connection.
*/
private static DatagramSocket[] UDP_ACCESS;
private NetworkManagerStub networkManagerStub;
@Inject private MessagesSupportedVendorMessage messagesSupportedVendorMessage;
@Inject private SearchServices searchServices;
@Inject private ResponseFactory responseFactory;
@Inject private QueryReplyFactory queryReplyFactory;
@Inject private ReplyNumberVendorMessageFactory replyNumberVendorMessageFactory;
@Inject private QueryRequestFactory queryRequestFactory;
@Inject private DownloadServices downloadServices;
@Inject private MessageRouter messageRouter;
@Inject private MessageFactory messageFactory;
@Inject private PingReplyFactory pingReplyFactory;
@Inject private OnDemandUnicaster onDemandUnicaster;
@Inject private MACCalculatorRepositoryManager macManager;
@Inject private RemoteFileDescFactory remoteFileDescFactory;
@Inject private PushEndpointFactory pushEndpointFactory;
public ClientSideOOBRequeryTest(String name) {
super(name);
}
public static Test suite() {
return buildTestSuite(ClientSideOOBRequeryTest.class);
}
public static void main(String[] args) {
junit.textui.TestRunner.run(suite());
}
@Override
public void setSettings() {
TIMEOUT = 4000;
ConnectionSettings.DO_NOT_BOOTSTRAP.setValue(true);
}
@Override
public void setUp() throws Exception {
networkManagerStub = new NetworkManagerStub();
Injector injector = LimeTestUtils.createInjector(MyCallback.class, new LimeTestUtils.NetworkManagerStubModule(networkManagerStub));
super.setUp(injector);
callback = (MyCallback) injector.getInstance(ActivityCallback.class);
networkManagerStub.setAcceptedIncomingConnection(true);
networkManagerStub.setCanReceiveSolicited(true);
networkManagerStub.setCanReceiveUnsolicited(true);
networkManagerStub.setOOBCapable(true);
networkManagerStub.setPort(SERVER_PORT);
File file = TestUtils.getResourceFile("com/limegroup/gnutella/metadata/metadata.mp3");
assertNotNull(gnutellaFileCollection.add(file).get(1, TimeUnit.SECONDS));
UDP_ACCESS = new DatagramSocket[10];
for (int i = 0; i < UDP_ACCESS.length; i++)
UDP_ACCESS[i] = new DatagramSocket();
for (int i = 0; i < testUP.length; i++) {
assertTrue("should be open", testUP[i].isOpen());
assertTrue("should be up -> leaf",
testUP[i].getConnectionCapabilities().isSupernodeClientConnection());
BlockingConnectionUtils.drain(testUP[i], 100);
// OOB client side needs server side leaf guidance
testUP[i].send(messagesSupportedVendorMessage);
testUP[i].flush();
}
Thread.sleep(250);
// we should now be guess capable and tcp incoming capable....
// test on acceptor since network manager is stubbed out
// assertTrue(injector.getInstance(Acceptor.class).acceptedIncoming());
}
public void testNoDownloadQueryDonePurge() throws Exception {
// set smaller clear times so we can test in a timely fashion
BlockingConnectionUtils.keepAllAlive(testUP, pingReplyFactory);
// clear up any messages before we begin the test.
drainAll();
Message m = null;
byte[] guid = searchServices.newQueryGUID();
searchServices.query(guid, "whatever");
// i need to pretend that the UI is showing the user the query still
callback.setGUID(new GUID(guid));
QueryRequest qr =
BlockingConnectionUtils.getFirstInstanceOfMessageType(testUP[0],
QueryRequest.class);
assertNotNull(qr);
assertTrue(qr.desiresOutOfBandReplies());
// ok, the leaf is sending OOB queries - good stuff, now we should send
// a lot of results back and make sure it buffers the bypassed OOB ones
for (int i = 0; i < testUP.length; i++) {
Response[] res = new Response[200];
for (int j = 0; j < res.length; j++)
res[j] = responseFactory.createResponse(10+j+i, 10+j+i, "whatever "+ j + i, UrnHelper.SHA1);
m = queryReplyFactory.createQueryReply(qr.getGUID(), (byte) 1, 6355,
myIP(), 0, res, GUID.makeGuid(), new byte[0], false, false,
true, true, false, false, null);
testUP[i].send(m);
testUP[i].flush();
}
// wait for processing
Thread.sleep(2000);
for (int i = 0; i < UDP_ACCESS.length; i++) {
ReplyNumberVendorMessage vm =
replyNumberVendorMessageFactory.createV3ReplyNumberVendorMessage(new GUID(qr.getGUID()), i+1);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
vm.write(baos);
DatagramPacket pack = new DatagramPacket(baos.toByteArray(),
baos.toByteArray().length,
testUP[0].getInetAddress(), SERVER_PORT);
UDP_ACCESS[i].send(pack);
}
// wait for processing
Thread.sleep(1000);
{
// all the UDP ReplyNumberVMs should have been bypassed
assertByPassedResultsCacheHasSize(qr.getGUID(), UDP_ACCESS.length);
}
{
// now we should make sure MessageRouter clears the map
searchServices.stopQuery(new GUID(qr.getGUID()));
assertByPassedResultsCacheHasSize(qr.getGUID(), 0);
}
callback.clearGUID();
}
public void testDownloadDoneQueryDonePurge() throws Exception {
BlockingConnectionUtils.keepAllAlive(testUP, pingReplyFactory);
// clear up any messages before we begin the test.
drainAll();
// luckily there is hacky little way to go through the download paces -
// download from yourself :) .
Message m = null;
byte[] guid = searchServices.newQueryGUID();
searchServices.query(guid, "berkeley");
callback.setGUID(new GUID(guid));
QueryRequest qr =
BlockingConnectionUtils.getFirstInstanceOfMessageType(testUP[0],
QueryRequest.class);
assertNotNull(qr);
assertTrue(qr.desiresOutOfBandReplies());
// just return ONE real result and the rest junk
{
// get a correct response object
QueryRequest qrTemp = queryRequestFactory.createQuery("berkeley");
testUP[0].send(qrTemp);
testUP[0].flush();
}
Response resp = null;
QueryReply reply = null;
reply = BlockingConnectionUtils.getFirstInstanceOfMessageType(testUP[0],
QueryReply.class);
assertNotNull(reply);
resp = (reply.getResultsAsList()).get(0);
assertNotNull(resp);
Response[] res = new Response[] { resp };
// this isn't really needed but just for completeness send it back to
// the test Leaf
m = queryReplyFactory.createQueryReply(guid, (byte) 1, SERVER_PORT,
myIP(), 0, res, GUID.makeGuid(), new byte[0], false, false,
true, true, false, false, null);
testUP[0].send(m);
testUP[0].flush();
// send back a lot of results via TCP so you konw the UDP one will be
// bypassed
for (int i = 0; i < testUP.length; i++) {
res = new Response[75];
for (int j = 0; j < res.length; j++)
res[j] = responseFactory.createResponse(10+j+i, 10+j+i, "berkeley "+ j + i, UrnHelper.SHA1);
m = queryReplyFactory.createQueryReply(guid, (byte) 1,
testUP[0].getPort(), myIP(), 0, res, GUID.makeGuid(), new byte[0],
false, false, true, true, false, false, null);
testUP[i].send(m);
testUP[i].flush();
}
// allow for processing
Thread.sleep(3000);
{
// now we should make sure MessageRouter has not bypassed anything
// yet
assertByPassedResultsCacheHasSize(qr.getGUID(), 0);
}
// send back a UDP response and make sure it was saved in bypassed...
{
ReplyNumberVendorMessage vm =
replyNumberVendorMessageFactory.createV3ReplyNumberVendorMessage(new GUID(guid), 1);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
vm.write(baos);
DatagramPacket pack = new DatagramPacket(baos.toByteArray(),
baos.toByteArray().length,
testUP[0].getInetAddress(),
SERVER_PORT);
UDP_ACCESS[0].send(pack);
}
// allow for processing
Thread.sleep(500);
{
// all the UDP ReplyNumberVMs should have been bypassed
assertByPassedResultsCacheHasSize(guid, 1);
}
// now do the download, wait for it to finish, and then bypassed results
// should be empty again
RemoteFileDesc rfd = resp.toRemoteFileDesc(reply, null, remoteFileDescFactory, pushEndpointFactory);
assertFalse("file should not be saved yet",
new File( _savedDir, "berkeley.txt").exists());
downloadServices.download(new RemoteFileDesc[] { rfd }, false, new GUID(guid));
callback.clearGUID();
// sleep to make sure the download starts
Thread.sleep(10000);
assertTrue("file should saved",
new File( _savedDir, "berkeley.txt").exists());
{
// now we should make sure MessageRouter clears the map
assertByPassedResultsCacheHasSize(qr.getGUID(), 0);
}
}
private void assertByPassedResultsCacheHasSize(byte[] guid, int size) {
Set<GUESSEndpoint> endpoints = messageRouter.getQueryLocs(new GUID(guid));
assertEquals(size, endpoints.size());
}
public void testQueryAliveNoPurge() throws Exception {
BlockingConnectionUtils.keepAllAlive(testUP, pingReplyFactory);
// clear up any messages before we begin the test.
drainAll();
// luckily there is hacky little way to go through the download paces -
// download from yourself :) .
Message m = null;
byte[] guid = searchServices.newQueryGUID();
searchServices.query(guid, "berkeley");
callback.setGUID(new GUID(guid));
QueryRequest qr =
BlockingConnectionUtils.getFirstInstanceOfMessageType(testUP[0],
QueryRequest.class);
assertNotNull(qr);
assertTrue(qr.desiresOutOfBandReplies());
// just return ONE real result and the rest junk
Response resp = null;
QueryReply reply = null;
{
// get a correct response object
QueryRequest qrTemp = queryRequestFactory.createQuery("berkeley");
testUP[0].send(qrTemp);
testUP[0].flush();
reply = BlockingConnectionUtils.getFirstInstanceOfMessageType(testUP[0],
QueryReply.class);
assertNotNull(reply);
resp = (reply.getResultsAsList()).get(0);
}
assertNotNull(reply);
assertNotNull(resp);
Response[] res = new Response[] { resp };
// this isn't really needed but just for completeness send it back to
// the test Leaf
m = queryReplyFactory.createQueryReply(guid, (byte) 1, SERVER_PORT,
myIP(), 0, res, GUID.makeGuid(), new byte[0], false, false,
true, true, false, false, null);
testUP[0].send(m);
testUP[0].flush();
// send back a lot of results via TCP so you konw the UDP one will be
// bypassed
for (int i = 0; i < testUP.length; i++) {
res = new Response[75];
for (int j = 0; j < res.length; j++)
res[j] = responseFactory.createResponse(10+j+i, 10+j+i, "berkeley "+ j + i, UrnHelper.SHA1);
m = queryReplyFactory.createQueryReply(guid, (byte) 1,
testUP[0].getPort(), myIP(), 0, res, GUID.makeGuid(), new byte[0],
false, false, true, true, false, false, null);
testUP[i].send(m);
testUP[i].flush();
}
// allow for processing
Thread.sleep(3000);
{
// now we should make sure MessageRouter has not bypassed anything
// yet
assertByPassedResultsCacheHasSize(qr.getGUID(), 0);
}
// send back a UDP response and make sure it was saved in bypassed...
{
ReplyNumberVendorMessage vm =
replyNumberVendorMessageFactory.createV3ReplyNumberVendorMessage(new GUID(guid), 1);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
vm.write(baos);
DatagramPacket pack = new DatagramPacket(baos.toByteArray(),
baos.toByteArray().length,
testUP[0].getInetAddress(),
SERVER_PORT);
UDP_ACCESS[0].send(pack);
}
// allow for processing
Thread.sleep(500);
{
// all the UDP ReplyNumberVMs should have been bypassed
assertByPassedResultsCacheHasSize(guid, 1);
}
// now do the download, wait for it to finish, and then bypassed results
// should not be empty since the query is still alive
RemoteFileDesc rfd = resp.toRemoteFileDesc(reply, null, remoteFileDescFactory, pushEndpointFactory);
assertFalse("file should not be saved yet",
new File( _savedDir, "berkeley.txt").exists());
Downloader d = downloadServices.download(new RemoteFileDesc[] { rfd }, false, new GUID(guid));
// sleep to make sure the download starts
int sleeps = 0;
while(sleeps++ < 30 && !d.isCompleted())
Thread.sleep(1000);
assertTrue("download took too long",d.isCompleted());
assertTrue("file should saved",
new File( _savedDir, "berkeley.txt").exists());
{
// all the UDP ReplyNumberVMs should have been bypassed
assertByPassedResultsCacheHasSize(guid, 1);
}
searchServices.stopQuery(new GUID(guid));
{
// now we should make sure MessageRouter clears the map
assertByPassedResultsCacheHasSize(qr.getGUID(),0);
}
}
public void testDownloadProgressQueryDoneNoPurge()
throws Exception {
BlockingConnectionUtils.keepAllAlive(testUP, pingReplyFactory);
// clear up any messages before we begin the test.
drainAll();
// luckily there is hacky little way to go through the download paces -
// download from yourself :) .
Message m = null;
byte[] guid = searchServices.newQueryGUID();
searchServices.query(guid, "metadata");
callback.setGUID(new GUID(guid));
QueryRequest qr =
BlockingConnectionUtils.getFirstInstanceOfMessageType(testUP[0],
QueryRequest.class);
assertNotNull(qr);
assertTrue(qr.desiresOutOfBandReplies());
// just return ONE real result and the rest junk
Response resp = null;
QueryReply reply = null;
{
// get a correct response object
QueryRequest qrTemp = queryRequestFactory.createQuery("metadata");
testUP[0].send(qrTemp);
testUP[0].flush();
reply = BlockingConnectionUtils.getFirstInstanceOfMessageType(testUP[0],
QueryReply.class);
assertNotNull(reply);
resp = (reply.getResultsAsList()).get(0);
}
assertNotNull(reply);
assertNotNull(resp);
Response[] res = new Response[] { resp };
// this isn't really needed but just for completeness send it back to
// the test Leaf
m = queryReplyFactory.createQueryReply(guid, (byte) 1, SERVER_PORT,
myIP(), 0, res, GUID.makeGuid(), new byte[0], false, false,
true, true, false, false, null);
testUP[0].send(m);
testUP[0].flush();
// send back a lot of results via TCP so you konw the UDP one will be
// bypassed
for (int i = 0; i < testUP.length; i++) {
res = new Response[75];
for (int j = 0; j < res.length; j++)
res[j] = responseFactory.createResponse(10+j+i, 10+j+i, "metadata "+ j + i, UrnHelper.SHA1);
m = queryReplyFactory.createQueryReply(guid, (byte) 1,
testUP[0].getPort(), myIP(), 0, res, GUID.makeGuid(), new byte[0],
false, false, true, true, false, false, null);
testUP[i].send(m);
testUP[i].flush();
}
// allow for processing
Thread.sleep(3000);
{
// now we should make sure MessageRouter has not bypassed anything
// yet
assertByPassedResultsCacheHasSize(qr.getGUID(), 0);
}
// send back a UDP response and make sure it was saved in bypassed...
{
ReplyNumberVendorMessage vm =
replyNumberVendorMessageFactory.createV3ReplyNumberVendorMessage(new GUID(guid), 1);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
vm.write(baos);
DatagramPacket pack = new DatagramPacket(baos.toByteArray(),
baos.toByteArray().length,
testUP[0].getInetAddress(),
SERVER_PORT);
UDP_ACCESS[0].send(pack);
}
// allow for processing
Thread.sleep(500);
{
// all the UDP ReplyNumberVMs should have been bypassed
assertByPassedResultsCacheHasSize(guid, 1);
}
// now do the download, wait for it to finish, and then bypassed results
// should be empty again
RemoteFileDesc rfd = resp.toRemoteFileDesc(reply, null, remoteFileDescFactory, pushEndpointFactory);
assertFalse("file should not be saved yet",
new File( _savedDir, "metadata.mp3").exists());
downloadServices.download(new RemoteFileDesc[] { rfd }, false, new GUID(guid));
UploadSettings.UPLOAD_SPEED.setValue(5);
searchServices.stopQuery(new GUID(guid));
callback.clearGUID();
// download still in progress, don't purge
assertByPassedResultsCacheHasSize(guid, 1);
UploadSettings.UPLOAD_SPEED.setValue(100);
// sleep to make sure the download starts
Thread.sleep(20000);
assertTrue("file should saved",
new File( _savedDir, "metadata.mp3").exists());
// now we should make sure MessageRouter clears the cache
assertByPassedResultsCacheHasSize(qr.getGUID(), 0);
}
public void testBusyDownloadLocatesSources() throws Exception {
BlockingConnectionUtils.keepAllAlive(testUP, pingReplyFactory);
// clear up any messages before we begin the test.
drainAll();
DatagramPacket pack = null;
Message m = null;
byte[] guid = searchServices.newQueryGUID();
searchServices.query(guid, "whatever");
// i need to pretend that the UI is showing the user the query still
callback.setGUID(new GUID(guid));
QueryRequest qr =
BlockingConnectionUtils.getFirstInstanceOfMessageType(testUP[0],
QueryRequest.class);
assertNotNull(qr);
assertTrue(qr.desiresOutOfBandReplies());
// ok, the leaf is sending OOB queries - good stuff, now we should send
// a lot of results back and make sure it buffers the bypassed OOB ones
for (int i = 0; i < testUP.length; i++) {
Response[] res = new Response[200];
for (int j = 0; j < res.length; j++)
res[j] = responseFactory.createResponse(10+j+i, 10+j+i, "whatever "+ j + i, UrnHelper.SHA1);
m = queryReplyFactory.createQueryReply(qr.getGUID(), (byte) 1, 6355,
myIP(), 0, res, GUID.makeGuid(), new byte[0], false, false,
true, true, false, false, null);
testUP[i].send(m);
testUP[i].flush();
}
// create a test uploader and send back that response
TestUploader uploader = new TestUploader(networkManagerStub);
uploader.start("whatever", UPLOADER_PORT, false);
uploader.setBusy(true);
RemoteFileDesc rfd = makeRFD("GLIQY64M7FSXBSQEZY37FIM5QQSA2OUJ");
// wait for processing
Thread.sleep(1500);
for (int i = 0; i < UDP_ACCESS.length; i++) {
ReplyNumberVendorMessage vm =
replyNumberVendorMessageFactory.createV3ReplyNumberVendorMessage(new GUID(qr.getGUID()), i+1);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
vm.write(baos);
pack = new DatagramPacket(baos.toByteArray(),
baos.toByteArray().length,
testUP[0].getInetAddress(), SERVER_PORT);
UDP_ACCESS[i].send(pack);
}
// wait for processing
Thread.sleep(500);
{
// all the UDP ReplyNumberVMs should have been bypassed
assertByPassedResultsCacheHasSize(qr.getGUID(), UDP_ACCESS.length);
}
Downloader downloader =
downloadServices.download(new RemoteFileDesc[] { rfd }, false,
new GUID(guid));
final int MAX_TRIES = 60;
for (int i = 0; i <= MAX_TRIES; i++) {
Thread.sleep(1000);
if (downloader.getState() == DownloadState.ITERATIVE_GUESSING)
break;
if (i == MAX_TRIES) fail("didn't GUESS!!");
}
// we should start getting guess queries on all UDP ports, actually
// querykey requests
for (int i = 0; i < UDP_ACCESS.length; i++) {
boolean gotPing = false;
while (!gotPing) {
try {
byte[] datagramBytes = new byte[1000];
pack = new DatagramPacket(datagramBytes, 1000);
UDP_ACCESS[i].setSoTimeout(10000); // may need to wait
UDP_ACCESS[i].receive(pack);
InputStream in = new ByteArrayInputStream(pack.getData());
m = messageFactory.read(in, Network.TCP);
m.hop();
if (m instanceof PingRequest)
gotPing = ((PingRequest) m).isQueryKeyRequest();
}
catch (InterruptedIOException iioe) {
fail("was successful for " + i, iioe);
}
}
}
//Thread.sleep((UDP_ACCESS.length * 1000) -
//(System.currentTimeMillis() - currTime));
int guessWaitTime = 5000;
Thread.sleep(guessWaitTime+2000);
assertEquals(DownloadState.BUSY, downloader.getState());
callback.clearGUID();
downloader.stop();
Thread.sleep(1000);
{
// now we should make sure MessageRouter clears the map
assertByPassedResultsCacheHasSize(qr.getGUID(), 0);
}
uploader.stopThread();
}
public void testDownloadFinishes() throws Exception {
BlockingConnectionUtils.keepAllAlive(testUP, pingReplyFactory);
// clear up any messages before we begin the test.
drainAll();
DatagramPacket pack = null;
Message m = null;
byte[] guid = searchServices.newQueryGUID();
searchServices.query(guid, "whatever");
// i need to pretend that the UI is showing the user the query still
callback.setGUID(new GUID(guid));
QueryRequest qr =
BlockingConnectionUtils.getFirstInstanceOfMessageType(testUP[0],
QueryRequest.class);
assertNotNull(qr);
assertTrue(qr.desiresOutOfBandReplies());
// ok, the leaf is sending OOB queries - good stuff, now we should send
// a lot of results back and make sure it buffers the bypassed OOB ones
for (int i = 0; i < testUP.length; i++) {
Response[] res = new Response[200];
for (int j = 0; j < res.length; j++)
res[j] = responseFactory.createResponse(10+j+i, 10+j+i, "whatever "+ j + i, UrnHelper.SHA1);
m = queryReplyFactory.createQueryReply(qr.getGUID(), (byte) 1, 6355,
myIP(), 0, res, GUID.makeGuid(), new byte[0], false, false,
true, true, false, false, null);
testUP[i].send(m);
testUP[i].flush();
}
// create a test uploader and send back that response
TestUploader uploader = new TestUploader(networkManagerStub);
uploader.start("whatever", UPLOADER_PORT, false);
uploader.setBusy(true);
URN urn = TestFile.hash();
RemoteFileDesc rfd = makeRFD(urn);
// wait for processing
Thread.sleep(1500);
// just do it for 1 UDP guy
{
ReplyNumberVendorMessage vm =
replyNumberVendorMessageFactory.createV3ReplyNumberVendorMessage(new GUID(qr.getGUID()), 1);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
vm.write(baos);
pack = new DatagramPacket(baos.toByteArray(),
baos.toByteArray().length,
testUP[0].getInetAddress(), SERVER_PORT);
UDP_ACCESS[0].send(pack);
}
// wait for processing
Thread.sleep(500);
{
// all the UDP ReplyNumberVMs should have been bypassed
assertByPassedResultsCacheHasSize(qr.getGUID(), 1);
}
long currTime = System.currentTimeMillis();
Downloader downloader =
downloadServices.download(new RemoteFileDesc[] { rfd }, false,
new GUID(guid));
final int MAX_TRIES = 60;
for (int i = 0; i <= MAX_TRIES; i++) {
Thread.sleep(500);
if (downloader.getState() == DownloadState.ITERATIVE_GUESSING)
break;
if (i == MAX_TRIES) fail("didn't GUESS!!");
}
// we should get a query key request
{
boolean gotPing = false;
while (!gotPing) {
byte[] datagramBytes = new byte[1000];
pack = new DatagramPacket(datagramBytes, 1000);
UDP_ACCESS[0].setSoTimeout(10000); // may need to wait
UDP_ACCESS[0].receive(pack);
InputStream in = new ByteArrayInputStream(pack.getData());
m = messageFactory.read(in, Network.TCP);
m.hop();
if (m instanceof PingRequest)
gotPing = ((PingRequest) m).isQueryKeyRequest();
}
}
// send back a query key
AddressSecurityToken qk = new AddressSecurityToken(InetAddress.getLocalHost(), SERVER_PORT, macManager);
{
byte[] ip = new byte[] {(byte)127, (byte) 0, (byte) 0, (byte) 1};
PingReply pr =
pingReplyFactory.createQueryKeyReply(GUID.makeGuid(), (byte) 1,
UDP_ACCESS[0].getLocalPort(),
ip, 10, 10, false, qk);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
pr.write(baos);
pack = new DatagramPacket(baos.toByteArray(),
baos.toByteArray().length,
testUP[0].getInetAddress(),
SERVER_PORT);
UDP_ACCESS[0].send(pack);
}
Thread.sleep(500);
// ensure that it gets into the OnDemandUnicaster
{
// now we should make sure MessageRouter retains the key
Map _queryKeys = (Map) PrivilegedAccessor.getValue(onDemandUnicaster, "_queryKeys");
assertNotNull(_queryKeys);
assertEquals(1, _queryKeys.size());
}
byte[] urnQueryGUID = null;
{ // confirm a URN query
boolean gotQuery = false;
while (!gotQuery) {
byte[] datagramBytes = new byte[1000];
pack = new DatagramPacket(datagramBytes, 1000);
UDP_ACCESS[0].setSoTimeout(10000); // may need to wait
UDP_ACCESS[0].receive(pack);
InputStream in = new ByteArrayInputStream(pack.getData());
m = messageFactory.read(in, Network.TCP);
if (m instanceof QueryRequest) {
QueryRequest qReq = (QueryRequest)m;
Set queryURNs = qReq.getQueryUrns();
gotQuery = queryURNs.contains(urn);
if (gotQuery) {
gotQuery = qReq.getQueryKey().isFor(InetAddress.getLocalHost(), SERVER_PORT);
if (gotQuery)
urnQueryGUID = qReq.getGUID();
}
}
}
}
assertNotNull(urnQueryGUID);
long timeoutVal = 8000 - (System.currentTimeMillis() - currTime);
Thread.sleep(timeoutVal > 0 ? timeoutVal : 0);
assertEquals(DownloadState.BUSY, downloader.getState());
// purge front end of query
callback.clearGUID();
// create a new Uploader to service the download
TestUploader uploader2 = new TestUploader(networkManagerStub);
uploader2.start("whatever", UPLOADER_PORT+1, false);
uploader2.setRate(100);
{ // send back a query request, the TestUploader should service upload
rfd = makeRFD(urn, UPLOADER_PORT + 1);
Response[] res = new Response[] { responseFactory.createResponse(10, 10, "whatever", urn) };
m = queryReplyFactory.createQueryReply(urnQueryGUID, (byte) 1,
UPLOADER_PORT+1, myIP(), 0, res, GUID.makeGuid(),
new byte[0], false, false, true, true, false, false, null);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
m.write(baos);
pack = new DatagramPacket(baos.toByteArray(),
baos.toByteArray().length,
testUP[0].getInetAddress(),
SERVER_PORT);
UDP_ACCESS[0].send(pack);
}
// after a while, the download should finish, the bypassed results
// should be discarded
Thread.sleep(10000);
assertEquals(DownloadState.COMPLETE, downloader.getState());
{
// now we should make sure MessageRouter clears the map
assertByPassedResultsCacheHasSize(qr.getGUID(), 0);
}
uploader.stopThread();
}
public void testUsesCachedQueryKeys() throws Exception {
BlockingConnectionUtils.keepAllAlive(testUP, pingReplyFactory);
// clear up any messages before we begin the test.
drainAll();
DatagramPacket pack = null;
Message m = null;
byte[] guid = searchServices.newQueryGUID();
searchServices.query(guid, "whatever");
// i need to pretend that the UI is showing the user the query still
callback.setGUID(new GUID(guid));
QueryRequest qr =
BlockingConnectionUtils.getFirstInstanceOfMessageType(testUP[0],
QueryRequest.class);
assertNotNull(qr);
assertTrue(qr.desiresOutOfBandReplies());
// ok, the leaf is sending OOB queries - good stuff, now we should send
// a lot of results back and make sure it buffers the bypassed OOB ones
for (int i = 0; i < testUP.length; i++) {
Response[] res = new Response[200];
for (int j = 0; j < res.length; j++)
res[j] = responseFactory.createResponse(10+j+i, 10+j+i, "whatever "+ j + i, UrnHelper.SHA1);
m = queryReplyFactory.createQueryReply(qr.getGUID(), (byte) 1, 6355,
myIP(), 0, res, GUID.makeGuid(), new byte[0], false, false,
true, true, false, false, null);
testUP[i].send(m);
testUP[i].flush();
}
// create a test uploader and send back that response
TestUploader uploader = new TestUploader(networkManagerStub);
uploader.start("whatever", UPLOADER_PORT, false);
uploader.setBusy(true);
URN urn = URN.createSHA1Urn("urn:sha1:GLIQY64M7FSXBSQEZY37FIM5QQSA2OUJ");
RemoteFileDesc rfd = makeRFD(urn);
// wait for processing
Thread.sleep(1500);
// send back ReplyNumberVMs that should be bypassed
for (int i = 0; i < UDP_ACCESS.length; i++) {
ReplyNumberVendorMessage vm =
replyNumberVendorMessageFactory.createV3ReplyNumberVendorMessage(new GUID(qr.getGUID()), i+1);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
vm.write(baos);
pack = new DatagramPacket(baos.toByteArray(),
baos.toByteArray().length,
testUP[0].getInetAddress(), SERVER_PORT);
UDP_ACCESS[i].send(pack);
}
// wait for processing
Thread.sleep(500);
{
// all the UDP ReplyNumberVMs should have been bypassed
assertByPassedResultsCacheHasSize(qr.getGUID(), UDP_ACCESS.length);
}
// Prepopulate Query Keys
AddressSecurityToken qk = new AddressSecurityToken(InetAddress.getLocalHost(),
SERVER_PORT, macManager);
for (int i = 0; i < UDP_ACCESS.length; i++) {
byte[] ip = new byte[] {(byte)127, (byte) 0, (byte) 0, (byte) 1};
PingReply pr =
pingReplyFactory.createQueryKeyReply(GUID.makeGuid(), (byte) 1,
UDP_ACCESS[i].getLocalPort(),
ip, 10, 10, false, qk);
pr.hop();
onDemandUnicaster.handleQueryKeyPong(pr);
}
// confirm download will try to GUESS
long currTime = System.currentTimeMillis();
Downloader downloader =
downloadServices.download(new RemoteFileDesc[] { rfd }, false,
new GUID(guid));
final int MAX_TRIES = 60;
for (int i = 0; i <= MAX_TRIES; i++) {
Thread.sleep(500);
if (downloader.getState() == DownloadState.ITERATIVE_GUESSING)
break;
if (i == MAX_TRIES) fail("didn't GUESS!!");
}
// we should start getting guess queries on all UDP ports
for (int i = 0; i < UDP_ACCESS.length; i++) {
boolean gotQuery = false;
while (!gotQuery) {
try {
byte[] datagramBytes = new byte[1000];
pack = new DatagramPacket(datagramBytes, 1000);
UDP_ACCESS[i].setSoTimeout(10000); // may need to wait
UDP_ACCESS[i].receive(pack);
InputStream in = new ByteArrayInputStream(pack.getData());
m = messageFactory.read(in, Network.TCP);
if (m instanceof QueryRequest) {
QueryRequest qReq = (QueryRequest)m;
Set queryURNs = qReq.getQueryUrns();
gotQuery = queryURNs.contains(urn);
if (gotQuery)
gotQuery = qReq.getQueryKey().isFor(InetAddress.getLocalHost(), SERVER_PORT);
}
}
catch (InterruptedIOException iioe) {
assertTrue("was successful for " + i,
false);
}
}
}
Thread.sleep((UDP_ACCESS.length * 1500) -
(System.currentTimeMillis() - currTime));
assertEquals(DownloadState.BUSY, downloader.getState());
callback.clearGUID();
downloader.stop();
Thread.sleep(1000);
{
// now we should make sure MessageRouter clears the map
assertByPassedResultsCacheHasSize(qr.getGUID(), 0);
}
uploader.stopThread();
}
public void testMultipleDownloadsNoPurge() throws Exception {
BlockingConnectionUtils.keepAllAlive(testUP, pingReplyFactory);
// clear up any messages before we begin the test.
drainAll();
DatagramPacket pack = null;
Message m = null;
byte[] guid = searchServices.newQueryGUID();
searchServices.query(guid, "whatever");
// i need to pretend that the UI is showing the user the query still
callback.setGUID(new GUID(guid));
QueryRequest qr =
BlockingConnectionUtils.getFirstInstanceOfMessageType(testUP[0],
QueryRequest.class);
assertNotNull(qr);
assertTrue(qr.desiresOutOfBandReplies());
// ok, the leaf is sending OOB queries - good stuff, now we should send
// a lot of results back and make sure it buffers the bypassed OOB ones
for (int i = 0; i < testUP.length; i++) {
Response[] res = new Response[200];
for (int j = 0; j < res.length; j++)
res[j] = responseFactory.createResponse(10+j+i, 10+j+i, "whatever "+ j + i, UrnHelper.SHA1);
m = queryReplyFactory.createQueryReply(qr.getGUID(), (byte) 1, 6355,
myIP(), 0, res, GUID.makeGuid(), new byte[0], false, false,
true, true, false, false, null);
testUP[i].send(m);
testUP[i].flush();
}
// create a test uploader and send back that response
TestUploader uploader = new TestUploader(networkManagerStub);
uploader.start("whatever", UPLOADER_PORT, false);
uploader.setBusy(true);
RemoteFileDesc rfd = makeRFD("GLIQY64M7FSXBSQEZY37FIM5QQSA2OUJ");
TestUploader uploader2 = new TestUploader(networkManagerStub);
uploader2.start("whatever", UPLOADER_PORT*2, false);
uploader2.setBusy(true);
RemoteFileDesc rfd2 = makeRFD("GLIQY64M7FSXBSQEZY37FIM5QQSASUSH");
// wait for processing
Thread.sleep(1500);
{ // bypass 1 result only
ReplyNumberVendorMessage vm =
replyNumberVendorMessageFactory.createV3ReplyNumberVendorMessage(new GUID(qr.getGUID()), 1);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
vm.write(baos);
pack = new DatagramPacket(baos.toByteArray(),
baos.toByteArray().length,
testUP[0].getInetAddress(), SERVER_PORT);
UDP_ACCESS[0].send(pack);
}
// wait for processing
Thread.sleep(500);
{
// all the UDP ReplyNumberVMs should have been bypassed
assertByPassedResultsCacheHasSize(qr.getGUID(), 1);
}
Downloader downloader =
downloadServices.download(new RemoteFileDesc[] { rfd }, false,
new GUID(guid));
// Don't try using the same default file
Downloader downloader2 =
downloadServices.download(new RemoteFileDesc[] { rfd2 },
new GUID(guid), false, null, "anotherFile" );
// let downloaders do stuff
final int MAX_TRIES = 60;
boolean oneGood = false, twoGood = false;
for (int i = 0; i <= MAX_TRIES; i++) {
Thread.sleep(500);
if (downloader.getState() == DownloadState.BUSY)
oneGood = true;
if (downloader2.getState() == DownloadState.BUSY)
twoGood = true;
if (oneGood && twoGood) break;
if (i == MAX_TRIES) fail("didn't GUESS!!");
}
callback.clearGUID(); // isQueryAlive == false
downloader.stop();
Thread.sleep(500);
{
// we should still have bypassed results since downloader2 alive
assertByPassedResultsCacheHasSize(qr.getGUID(), 1);
}
downloader2.stop();
Thread.sleep(1000);
{
// now we should make sure MessageRouter clears the map
assertByPassedResultsCacheHasSize(qr.getGUID(), 0);
}
uploader.stopThread();
uploader2.stopThread();
}
// RUN THIS TEST LAST!!
// TODO move this test case to OnDemandUnicasterTest, sounds like a pure unit test
// proabably doesn't make sense anymore since it doesn't have any data from the
// previous tests to clear
public void testUnicasterClearingCode() throws Exception {
BlockingConnectionUtils.keepAllAlive(testUP, pingReplyFactory);
// clear up any messages before we begin the test.
drainAll();
{ // clear all the unicaster data structures
Long[] longs = new Long[] { new Long(0), new Long(1) };
Class[] classTypes = new Class[] { Long.TYPE, Long.TYPE };
// now confirm that clearing code works
Object ret = PrivilegedAccessor.invokeMethod(onDemandUnicaster,
"clearDataStructures",
longs, classTypes);
assertTrue(ret instanceof Boolean);
assertTrue(((Boolean)ret).booleanValue());
}
DatagramPacket pack = null;
Message m = null;
byte[] guid = searchServices.newQueryGUID();
searchServices.query(guid, "whatever");
// i need to pretend that the UI is showing the user the query still
callback.setGUID(new GUID(guid));
QueryRequest qr =
BlockingConnectionUtils.getFirstInstanceOfMessageType(testUP[0],
QueryRequest.class);
assertNotNull(qr);
assertTrue(qr.desiresOutOfBandReplies());
// ok, the leaf is sending OOB queries - good stuff, now we should send
// a lot of results back and make sure it buffers the bypassed OOB ones
for (int i = 0; i < testUP.length; i++) {
Response[] res = new Response[200];
for (int j = 0; j < res.length; j++)
res[j] = responseFactory.createResponse(10+j+i, 10+j+i, "whatever "+ j + i, UrnHelper.SHA1);
m = queryReplyFactory.createQueryReply(qr.getGUID(), (byte) 1, 6355,
myIP(), 0, res, GUID.makeGuid(), new byte[0], false, false,
true, true, false, false, null);
testUP[i].send(m);
testUP[i].flush();
}
// create a test uploader and send back that response
TestUploader uploader = new TestUploader(networkManagerStub);
uploader.start("whatever", UPLOADER_PORT, false);
uploader.setBusy(true);
RemoteFileDesc rfd = makeRFD("GLIQY64M7FSXBSQEZY37FIM5QQSA2OUJ");
// wait for processing
Thread.sleep(1500);
for (int i = 0; i < UDP_ACCESS.length; i++) {
ReplyNumberVendorMessage vm =
replyNumberVendorMessageFactory.createV3ReplyNumberVendorMessage(new GUID(qr.getGUID()), i+1);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
vm.write(baos);
pack = new DatagramPacket(baos.toByteArray(),
baos.toByteArray().length,
testUP[0].getInetAddress(), SERVER_PORT);
UDP_ACCESS[i].send(pack);
}
// wait for processing
Thread.sleep(500);
{
// all the UDP ReplyNumberVMs should have been bypassed
assertByPassedResultsCacheHasSize(qr.getGUID(), UDP_ACCESS.length);
}
Downloader downloader =
downloadServices.download(new RemoteFileDesc[] { rfd }, false,
new GUID(guid));
final int MAX_TRIES = 60;
for (int i = 0; i <= MAX_TRIES; i++) {
Thread.sleep(500);
if (downloader.getState() == DownloadState.ITERATIVE_GUESSING)
break;
if (i == MAX_TRIES) fail("didn't GUESS!!");
}
// we should start getting guess queries on all UDP ports, actually
// querykey requests
for (int i = 0; i < UDP_ACCESS.length; i++) {
boolean gotPing = false;
while (!gotPing) {
try {
byte[] datagramBytes = new byte[1000];
pack = new DatagramPacket(datagramBytes, 1000);
UDP_ACCESS[i].setSoTimeout(10000); // may need to wait
UDP_ACCESS[i].receive(pack);
InputStream in = new ByteArrayInputStream(pack.getData());
m = messageFactory.read(in, Network.TCP);
m.hop();
if (m instanceof PingRequest)
gotPing = ((PingRequest) m).isQueryKeyRequest();
}
catch (InterruptedIOException iioe) {
assertTrue("was successful for " + i,
false);
}
}
}
// Prepopulate Query Keys
AddressSecurityToken qk = new AddressSecurityToken(InetAddress.getLocalHost(),
SERVER_PORT, macManager);
for (int i = 0; i < (UDP_ACCESS.length/2); i++) {
byte[] ip = new byte[] {(byte)127, (byte) 0, (byte) 0, (byte) 1};
PingReply pr =
pingReplyFactory.createQueryKeyReply(GUID.makeGuid(), (byte) 1,
UDP_ACCESS[i].getLocalPort(),
ip, 10, 10, false, qk);
pr.hop();
onDemandUnicaster.handleQueryKeyPong(pr);
}
// ensure that it gets into the OnDemandUnicaster
{
// now we should make sure MessageRouter retains the key
Map _queryKeys =
(Map) PrivilegedAccessor.getValue(onDemandUnicaster,
"_queryKeys");
assertNotNull(_queryKeys);
assertEquals((UDP_ACCESS.length/2), _queryKeys.size());
// now make sure some URNs are still buffered
Map _bufferedURNs =
(Map) PrivilegedAccessor.getValue(onDemandUnicaster,
"_bufferedURNs");
assertNotNull(_bufferedURNs);
assertEquals((UDP_ACCESS.length/2), _bufferedURNs.size());
}
// now until those guys get expired
Thread.sleep(60 * 1000);
{
Long[] longs = new Long[] { new Long(0), new Long(1) };
Class[] classTypes = new Class[] { Long.TYPE, Long.TYPE };
// now confirm that clearing code works
Object ret = PrivilegedAccessor.invokeMethod(onDemandUnicaster,
"clearDataStructures",
longs, classTypes);
assertTrue(ret instanceof Boolean);
assertTrue(((Boolean)ret).booleanValue());
}
// ensure that clearing worked
{
// now we should make sure MessageRouter retains the key
Map _queryKeys =
(Map) PrivilegedAccessor.getValue(onDemandUnicaster,
"_queryKeys");
assertNotNull(_queryKeys);
assertEquals(0, _queryKeys.size());
// now make sure some URNs are still buffered
Map _bufferedURNs =
(Map) PrivilegedAccessor.getValue(onDemandUnicaster,
"_bufferedURNs");
assertNotNull(_bufferedURNs);
assertEquals(0, _bufferedURNs.size());
}
assertEquals(DownloadState.BUSY, downloader.getState());
callback.clearGUID();
downloader.stop();
Thread.sleep(1000);
{
// now we should make sure MessageRouter clears the map
assertByPassedResultsCacheHasSize(qr.getGUID(), 0);
}
}
//////////////////////////////////////////////////////////////////
private RemoteFileDesc makeRFD(URN urn, int port) throws Exception {
Set<URN> urns = new HashSet<URN>();
urns.add(urn);
return injector.getInstance(RemoteFileDescFactory.class).createRemoteFileDesc(new ConnectableImpl("127.0.0.1", port, false), 1, "whatever", 10, GUID.makeGuid(), 1,
3, false, null, urns, false, "LIME", -1);
}
private RemoteFileDesc makeRFD(String sha1) throws Exception {
URN urn = URN.createSHA1Urn("urn:sha1:" + sha1);
return makeRFD(urn);
}
private RemoteFileDesc makeRFD(URN urn) throws Exception {
return makeRFD(urn, UPLOADER_PORT);
}
@Override
public int getNumberOfPeers() {
return 3;
}
private static byte[] myIP() {
return new byte[] { (byte)127, (byte)0, 0, 1 };
}
@Singleton
public static class MyCallback extends ActivityCallbackStub {
public GUID aliveGUID = null;
public void setGUID(GUID guid) { aliveGUID = guid; }
public void clearGUID() { aliveGUID = null; }
@Override
public boolean isQueryAlive(GUID guid) {
if (aliveGUID != null)
return (aliveGUID.equals(guid));
return false;
}
}
}