package com.limegroup.gnutella;
import static com.limegroup.gnutella.ConnectionManagerImpl.MAX_TCP_CONNECT_BACK_ATTEMPTS;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Random;
import junit.framework.Test;
import org.limewire.core.settings.ConnectionSettings;
import org.limewire.gnutella.tests.LimeTestUtils;
import org.limewire.io.GUID;
import org.limewire.util.StringUtils;
import com.google.inject.Injector;
import com.limegroup.gnutella.connection.BlockingConnection;
import com.limegroup.gnutella.messages.Message;
import com.limegroup.gnutella.messages.PingRequest;
import com.limegroup.gnutella.messages.PingRequestFactory;
import com.limegroup.gnutella.messages.vendor.MessagesSupportedVendorMessage;
import com.limegroup.gnutella.messages.vendor.ReplyNumberVendorMessage;
import com.limegroup.gnutella.messages.vendor.ReplyNumberVendorMessageFactory;
import com.limegroup.gnutella.messages.vendor.TCPConnectBackVendorMessage;
import com.limegroup.gnutella.messages.vendor.UDPConnectBackVendorMessage;
/**
* Checks whether leaves request redirects properly.
* The test includes a leaf attached to two ultrapeers.
*/
public class ClientSideValidateIncomingTest extends ClientSideTestCase {
protected final int PORT=6669;
{
TIMEOUT = 1000; // should override super
}
private static final long MY_EXPIRE_TIME = 6 * 1000;
private static final long MY_WAIT_TIME = 500;
private static final long MY_VALIDATE_TIME = 3 * 1000;
private static byte[] cbGuid = null;
private AcceptorImpl acceptor;
private MessagesSupportedVendorMessage messagesSupportedVendorMessage;
private NetworkManager networkManager;
private UDPService udpService;
private ConnectionManager connectionManager;
private PingRequestFactory pingRequestFactory;
private ReplyNumberVendorMessageFactory replyNumberVendorMessageFactory;
public ClientSideValidateIncomingTest(String name) {
super(name);
}
public static Test suite() {
return buildTestSuite(ClientSideValidateIncomingTest.class);
}
public static void main(String[] args) {
junit.textui.TestRunner.run(suite());
}
@Override
public void setUp() throws Exception {
Injector injector = LimeTestUtils.createInjector();
acceptor = (AcceptorImpl)injector.getInstance(Acceptor.class);
// set values before everything is initialized
acceptor.setIncomingExpireTime(MY_EXPIRE_TIME);
acceptor.setWaitTimeAfterRequests(MY_WAIT_TIME);
acceptor.setTimeBetweenValidates(MY_VALIDATE_TIME);
super.setUp(injector);
messagesSupportedVendorMessage = injector.getInstance(MessagesSupportedVendorMessage.class);
networkManager = injector.getInstance(NetworkManager.class);
udpService = injector.getInstance(UDPService.class);
connectionManager = injector.getInstance(ConnectionManager.class);
pingRequestFactory = injector.getInstance(PingRequestFactory.class);
replyNumberVendorMessageFactory = injector.getInstance(ReplyNumberVendorMessageFactory.class);
exchangeSupportedMessages();
}
@Override
public void setSettings() throws Exception {
ConnectionSettings.LOCAL_IS_PRIVATE.setValue(false);
}
private void exchangeSupportedMessages() throws Exception {
// send a MessagesSupportedMessage
testUP[0].send(messagesSupportedVendorMessage);
testUP[0].flush();
testUP[1].send(messagesSupportedVendorMessage);
testUP[1].flush();
// we expect to get a TCPConnectBack request
Message m = null;
boolean gotTCP = false, gotUDP = false;
do {
m = testUP[0].receive(TIMEOUT);
if (m instanceof TCPConnectBackVendorMessage) gotTCP = true;
if (m instanceof UDPConnectBackVendorMessage) {
cbGuid = m.getGUID();
gotUDP = true;
}
} while (!gotTCP || !gotUDP);
// client side seems to follow the setup process A-OK
}
/**
* Tests that only a limited number of connect back messages are
* sent upon connection initialization.
*/
public void testLimitedConnectBacksSent() throws Exception {
final int max = MAX_TCP_CONNECT_BACK_ATTEMPTS + 3; // not exact
int received = 0;
for (int i = 0; i < max; i++) {
testUP[0].send(messagesSupportedVendorMessage);
testUP[0].flush();
testUP[1].send(messagesSupportedVendorMessage);
testUP[1].flush();
while(BlockingConnectionUtils.getFirstInstanceOfMessageType(testUP[0], TCPConnectBackVendorMessage.class, 100) != null)
received++;
while(BlockingConnectionUtils.getFirstInstanceOfMessageType(testUP[1], TCPConnectBackVendorMessage.class, 100) != null)
received++;
}
assertLessThan(max,received);
}
// This test checks that if _acceptedIncoming is false, connect back
// messages are NOT sent
public void testTCPExpireRequestsNotSent() throws Exception {
drainAll();
for (int i = 0; i < 2; i++) {
assertFalse(networkManager.acceptedIncomingConnection());
try {
testUP[0].receive(TIMEOUT);
}
catch (InterruptedIOException expected) {}
Thread.sleep(MY_EXPIRE_TIME+MY_VALIDATE_TIME);
assertFalse(networkManager.acceptedIncomingConnection());
Thread.sleep(100);
// now we should get the connect backs cuz it has been a while
Message m = null;
do {
m = testUP[0].receive(TIMEOUT);
} while (!(m instanceof TCPConnectBackVendorMessage));
}
}
// make an incoming to the servent, wait a little, and then make sure
// it asks for a connect back again
public void testTCPExpireRequestsSent() throws Exception {
drainAll();
for (int i = 0; i < 2; i++) {
Socket s = new Socket("localhost", PORT);
s.close();
Thread.sleep(100);
// Socket must have said CONNECT BACK
assertFalse(networkManager.acceptedIncomingConnection());
s = new Socket("localhost", PORT);
s.getOutputStream().write(StringUtils.toAsciiBytes("CONNECT BACK\r\r"));
Thread.sleep(500);
s.close();
// Socket must have said CONNECT BACK
assertTrue(networkManager.acceptedIncomingConnection());
// wait until the expire time is realized
Thread.sleep(MY_EXPIRE_TIME + MY_VALIDATE_TIME + 1000);
// it should send off more requests
assertFalse(networkManager.acceptedIncomingConnection());
Message m = null;
do {
m = testUP[0].receive(TIMEOUT);
} while (!(m instanceof TCPConnectBackVendorMessage)) ;
}
}
/**
* Tests that if the leaf is connected to only one ultrapeer it will
* send a few redundant requests
*/
public void testTCPRedundantRequestsSent() throws Exception {
BlockingConnectionUtils.drainAllParallel(testUP);
// wait some time - both UPs should get a single connect back
//sleep
Thread.sleep(MY_EXPIRE_TIME+1000);
readNumConnectBacks(1,testUP[0], TIMEOUT);
readNumConnectBacks(1,testUP[1], TIMEOUT);
// leave only one connection open
assertGreaterThan(1,connectionManager.getNumInitializedConnections());
testUP[1].close();
Thread.sleep(500);
assertEquals(1,connectionManager.getNumInitializedConnections());
drainAll();
// sleep
Thread.sleep(MY_VALIDATE_TIME+1000);
// we should receive more than one connect back redirects
readNumConnectBacks(ConnectionManager.CONNECT_BACK_REDUNDANT_REQUESTS,testUP[0],TIMEOUT);
}
private void readNumConnectBacks(int num,BlockingConnection conn, int timeout) throws Exception {
Message m;
for (int i = 0; i < num; i++) {
do {
m = conn.receive(timeout);
} while (!(m instanceof TCPConnectBackVendorMessage));
}
try {
do {
m = conn.receive(timeout);
} while (!(m instanceof TCPConnectBackVendorMessage));
fail ("got extra message "+m.getClass()+" on "+conn);
} catch (IOException expected) {}
}
// This test checks that if _acceptedUnsolicitedIncoming is false, connect
// back messages are NOT sent
public void testUDPExpireRequestsNotSent() throws Exception {
drainAll();
UDPService udpServ = udpService;
for (int i = 0; i < 2; i++) {
assertFalse(udpServ.canReceiveUnsolicited());
try {
testUP[0].receive(TIMEOUT);
}
catch (InterruptedIOException expected) {}
Thread.sleep(MY_EXPIRE_TIME+MY_VALIDATE_TIME);
assertFalse(udpServ.canReceiveUnsolicited());
Thread.sleep(100);
// now we should get the connect backs cuz it has been a while
Message m = null;
do {
m = testUP[0].receive(TIMEOUT);
} while (!(m instanceof UDPConnectBackVendorMessage));
cbGuid = m.getGUID();
}
}
// make an incoming to the servent, wait a little, and then make sure
// it asks for a connect back again
public void testUDPExpireRequestsSent() throws Exception {
drainAll();
UDPService udpServ = udpService;
for (int i = 0; i < 2; i++) {
// wait until the expire time is realized
// drain the UDP buffer
Message m = null;
try {
while (true) {
m = testUP[0].receive(200);
}
}
catch (InterruptedIOException drained) {}
// get the UDP message
do {
m = testUP[0].receive();
} while (!(m instanceof UDPConnectBackVendorMessage)) ;
Thread.sleep(1000);
assertFalse(udpServ.canReceiveUnsolicited());
// get the UDP message but this time answer it
do {
m = testUP[0].receive();
} while (!(m instanceof UDPConnectBackVendorMessage)) ;
cbGuid =
((UDPConnectBackVendorMessage)m).getConnectBackGUID().bytes();
// now connect back and it should switch on unsolicited
DatagramSocket s = new DatagramSocket();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PingRequest ping = pingRequestFactory.createPingRequest(cbGuid, (byte)1,
(byte)1);
ping.write(baos);
DatagramPacket pack = new DatagramPacket(baos.toByteArray(),
baos.toByteArray().length,
InetAddress.getLocalHost(),
PORT);
s.send(pack);
s.close();
Thread.sleep(1000);
assertTrue(udpServ.canReceiveUnsolicited());
}
}
// make an incoming to the servent, wait a little, and then make sure
// it asks for a connect back again
public void testUDPInterleavingRequestsSent() throws Exception {
drainAll();
UDPService udpServ = udpService;
Random rand = new Random();
for (int i = 0; i < 6; i++) {
if (rand.nextBoolean()) {
DatagramSocket s = new DatagramSocket();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ReplyNumberVendorMessage vm =
replyNumberVendorMessageFactory.create(
new GUID(cbGuid), 1);
vm.write(baos);
DatagramPacket pack =
new DatagramPacket(baos.toByteArray(),
baos.toByteArray().length,
InetAddress.getLocalHost(), PORT);
s.send(pack);
s.close();
Thread.sleep(100);
}
// wait until the expire time is realized
Thread.sleep(MY_EXPIRE_TIME + MY_VALIDATE_TIME);
// throw some randomness in there - if we get an incoming we should
// not send messages out
if (udpServ.canReceiveUnsolicited()) {
DatagramSocket s = new DatagramSocket();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ReplyNumberVendorMessage vm =
replyNumberVendorMessageFactory.create(
new GUID(cbGuid), 1);
vm.write(baos);
DatagramPacket pack =
new DatagramPacket(baos.toByteArray(),
baos.toByteArray().length,
InetAddress.getLocalHost(), PORT);
s.send(pack);
s.close();
Thread.sleep(100);
assertTrue(udpServ.canReceiveUnsolicited());
try {
testUP[0].receive(TIMEOUT);
}
catch (InterruptedIOException expected) {}
}
else {
Thread.sleep(MY_VALIDATE_TIME);
// query the Acceptor - it should send off more requests
Thread.sleep(100);
Message m = null;
do {
m = testUP[0].receive(TIMEOUT);
} while (!(m instanceof UDPConnectBackVendorMessage)) ;
cbGuid =
((UDPConnectBackVendorMessage)m).getConnectBackGUID().bytes();
// now connect back and it should switch on unsolicited
DatagramSocket s = new DatagramSocket();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PingRequest ping = pingRequestFactory.createPingRequest(cbGuid,
(byte)1, (byte)1);
ping.write(baos);
DatagramPacket pack =
new DatagramPacket(baos.toByteArray(),
baos.toByteArray().length,
InetAddress.getLocalHost(), PORT);
s.send(pack);
s.close();
}
}
}
@Override
public int getNumberOfPeers() {
return 2;
}
}