package com.limegroup.gnutella;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Iterator;
import junit.framework.Test;
import org.limewire.gnutella.tests.LimeTestUtils;
import org.limewire.gnutella.tests.NetworkManagerStub;
import org.limewire.io.GUID;
import org.limewire.security.AddressSecurityToken;
import org.limewire.security.MACCalculatorRepositoryManager;
import org.limewire.util.TestUtils;
import com.google.inject.Injector;
import com.limegroup.gnutella.helpers.UrnHelper;
import com.limegroup.gnutella.library.UrnCache;
import com.limegroup.gnutella.messages.Message;
import com.limegroup.gnutella.messages.MessageFactory;
import com.limegroup.gnutella.messages.PingReply;
import com.limegroup.gnutella.messages.PingRequestFactory;
import com.limegroup.gnutella.messages.QueryReply;
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;
/**
* Checks whether a leaf node handles GUESS queries correctly. Tests the
* Server side of GUESS.
*/
public class ServerSideLeafGuessTest extends ClientSideTestCase {
/**
* Constant for the size of UDP messages to accept -- dependent upon
* IP-layer fragmentation.
*/
private final int BUFFER_SIZE = 8192;
/**
* Ultrapeer 1 UDP connection.
*/
private static DatagramSocket UDP_ACCESS;
private MessagesSupportedVendorMessage messagesSupportedVendorMessage;
private NetworkManagerStub networkManagerStub;
private PingRequestFactory pingRequestFactory;
private QueryRequestFactory queryRequestFactory;
private UrnCache urnCache;
private MessageFactory messageFactory;
private MACCalculatorRepositoryManager macManager;
public ServerSideLeafGuessTest(String name) {
super(name);
}
public static Test suite() {
return buildTestSuite(ServerSideLeafGuessTest.class);
}
public static void main(String[] args) {
junit.textui.TestRunner.run(suite());
}
@Override
public void setUp() throws Exception {
networkManagerStub = new NetworkManagerStub();
Injector injector = LimeTestUtils.createInjector(new LimeTestUtils.NetworkManagerStubModule(networkManagerStub));
super.setUp(injector);
messagesSupportedVendorMessage = injector.getInstance(MessagesSupportedVendorMessage.class);
pingRequestFactory = injector.getInstance(PingRequestFactory.class);
queryRequestFactory = injector.getInstance(QueryRequestFactory.class);
urnCache = injector.getInstance(UrnCache.class);
messageFactory = injector.getInstance(MessageFactory.class);
macManager = injector.getInstance(MACCalculatorRepositoryManager.class);
// first we need to set up GUESS capability
networkManagerStub.setCanReceiveSolicited(true);
networkManagerStub.setCanReceiveUnsolicited(true);
networkManagerStub.setGuessCapable(true);
networkManagerStub.setAcceptedIncomingConnection(true);
UDP_ACCESS = new DatagramSocket();
UDP_ACCESS.setSoTimeout(1000 * 20);
for (int i = 0; i < testUP.length; i++) {
BlockingConnectionUtils.drain(testUP[i]);
// OOB client side needs server side leaf guidance
testUP[i].send(messagesSupportedVendorMessage);
testUP[i].flush();
}
}
///////////////////////// Actual Tests ////////////////////////////
// MUST RUN THIS TEST FIRST
public void testBasicProtocol() throws Exception {
InetAddress localHost = InetAddress.getLocalHost();
// first send a AddressSecurityToken request....
send(pingRequestFactory.createQueryKeyRequest(), localHost, SERVER_PORT);
// we should get a AddressSecurityToken....
Message m = null;
AddressSecurityToken qkToUse = null;
while (true) {
m = receive();
if (m instanceof PingReply) {
PingReply rep = (PingReply) m;
qkToUse = rep.getQueryKey();
if (rep.getQueryKey() != null)
break;
}
}
assertNotNull(qkToUse);
// we should be able to send a query
QueryRequest goodQuery = queryRequestFactory.createQueryKeyQuery("susheel",
qkToUse);
byte[] guid = goodQuery.getGUID();
send(goodQuery, localHost, SERVER_PORT);
// now we should get an ack
m = receive();
assertTrue(m instanceof PingReply);
PingReply pRep = (PingReply) m;
assertEquals(new GUID(guid), new GUID(pRep.getGUID()));
// followed by a query hit
m = receive();
assertTrue(m instanceof QueryReply);
QueryReply qRep = (QueryReply) m;
assertEquals(new GUID(guid), new GUID(qRep.getGUID()));
}
public void testGoodURNQuery() throws Exception {
InetAddress localHost = InetAddress.getLocalHost();
// first send a AddressSecurityToken request....
send(pingRequestFactory.createQueryKeyRequest(), localHost, SERVER_PORT);
// we should get a AddressSecurityToken....
Message m = null;
AddressSecurityToken qkToUse = null;
while (true) {
m = receive();
if (m instanceof PingReply) {
PingReply rep = (PingReply) m;
qkToUse = rep.getQueryKey();
if (rep.getQueryKey() != null)
break;
}
}
assertNotNull(qkToUse);
// now send a URN query, make sure that works....
File berkeley = TestUtils.getResourceFile("com/limegroup/gnutella/berkeley.txt");
Iterator iter = UrnHelper.calculateAndCacheURN(berkeley, urnCache).iterator();
URN berkeleyURN = (URN) iter.next();
while (!berkeleyURN.isSHA1())
berkeleyURN = (URN) iter.next();
// we should be able to send a URN query
QueryRequest goodQuery = queryRequestFactory.createQueryKeyQuery(berkeleyURN,
qkToUse);
byte[] guid = goodQuery.getGUID();
send(goodQuery, localHost, SERVER_PORT);
// now we should get an ack
m = receive();
assertTrue(m instanceof PingReply);
PingReply pRep = (PingReply) m;
assertEquals(new GUID(guid), new GUID(pRep.getGUID()));
// followed by a query hit with a URN
m = receive();
assertTrue(m instanceof QueryReply);
QueryReply qRep = (QueryReply) m;
assertEquals(new GUID(guid), new GUID(qRep.getGUID()));
iter = qRep.getResults();
Response first = (Response) iter.next();
assertTrue(UrnHelper.calculateAndCacheURN(berkeley, urnCache).containsAll(first.getUrns()));
}
public void testQueryWithNoHit() throws Exception {
InetAddress localHost = InetAddress.getLocalHost();
// first send a AddressSecurityToken request....
send(pingRequestFactory.createQueryKeyRequest(), localHost, SERVER_PORT);
// we should get a AddressSecurityToken....
Message m = null;
AddressSecurityToken qkToUse = null;
while (true) {
m = receive();
if (m instanceof PingReply) {
PingReply rep = (PingReply) m;
qkToUse = rep.getQueryKey();
if (rep.getQueryKey() != null)
break;
}
}
assertNotNull(qkToUse);
// send a query that shouldn't get results....
QueryRequest goodQuery = queryRequestFactory.createQueryKeyQuery("anita",
qkToUse);
byte[] guid = goodQuery.getGUID();
send(goodQuery, localHost, SERVER_PORT);
// now we should get an ack
m = receive();
assertTrue(m instanceof PingReply);
PingReply pRep = (PingReply) m;
assertEquals(new GUID(guid), new GUID(pRep.getGUID()));
// but not a query hit
try {
m = receive();
assertTrue(false);
}
catch (InterruptedIOException expected) {};
}
public void testBadQueryKey() throws Exception {
InetAddress localHost = InetAddress.getLocalHost();
AddressSecurityToken qkToUse = new AddressSecurityToken(localHost, 0, macManager);
assertNotNull(qkToUse);
{
// we shouldn't get any response to our query...
QueryRequest badQuery = queryRequestFactory.createQueryKeyQuery("susheel",
qkToUse);
send(badQuery, localHost, SERVER_PORT);
try {
// now we should NOT get an ack
receive();
assertTrue(false);
} catch (InterruptedIOException expected) {}
}
}
//////////////////////////////////////////////////////////////////
private Message receive() throws Exception {
byte[] datagramBytes = new byte[BUFFER_SIZE];
DatagramPacket datagram = new DatagramPacket(datagramBytes,
BUFFER_SIZE);
UDP_ACCESS.receive(datagram);
byte[] data = datagram.getData();
// construct a message out of it...
InputStream in = new ByteArrayInputStream(data);
Message message = messageFactory.read(in, Network.TCP);
return message;
}
private void send(Message msg, InetAddress ip, int port)
throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
msg.write(baos);
byte[] data = baos.toByteArray();
DatagramPacket dg = new DatagramPacket(data, data.length, ip, port);
UDP_ACCESS.send(dg);
}
@Override
public int getNumberOfPeers() {
return 3;
}
}