package com.limegroup.gnutella;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Iterator;
import junit.framework.Test;
import com.limegroup.gnutella.handshaking.LeafHeaders;
import com.limegroup.gnutella.messages.Message;
import com.limegroup.gnutella.messages.PingRequest;
import com.limegroup.gnutella.messages.vendor.MessagesSupportedVendorMessage;
import com.limegroup.gnutella.messages.vendor.TCPConnectBackRedirect;
import com.limegroup.gnutella.messages.vendor.UDPConnectBackRedirect;
import com.limegroup.gnutella.routing.QueryRouteTable;
import com.limegroup.gnutella.routing.RouteTableMessage;
import com.limegroup.gnutella.stubs.ActivityCallbackStub;
import com.limegroup.gnutella.util.EmptyResponder;
import com.limegroup.gnutella.util.IOUtils;
/**
* Tests that an Ultrapeer correctly handles connect back redirect messages.
*
* ULTRAPEER[0] ---- CENTRAL TEST ULTRAPEER ---- ULTRAPEER[1]
* |
* |
* |
* LEAF
*
* This test only covers Ultrapeer behavior - leaves don't participate in
* server side connect back stuff.
*/
public final class ServerSideConnectBackRedirectTest extends ServerSideTestCase {
protected static int TIMEOUT = 2000;
/**
* Ultrapeer 1 UDP connection.
*/
private static DatagramSocket UDP_ACCESS;
/**
* Just a TCP connection to use for testing.
*/
private static ServerSocket TCP_ACCESS;
/**
* The port for TCP_ACCESS
*/
private static final int TCP_ACCESS_PORT = 10776;
public ServerSideConnectBackRedirectTest(String name) {
super(name);
}
public static Test suite() {
return buildTestSuite(ServerSideConnectBackRedirectTest.class);
}
public static void main(String[] args) {
junit.textui.TestRunner.run(suite());
}
public static Integer numUPs() {
return new Integer(2);
}
public static Integer numLeaves() {
return new Integer(1);
}
public static ActivityCallback getActivityCallback() {
return new ActivityCallbackStub();
}
public static void setUpQRPTables() throws Exception {
}
// BEGIN TESTS
// ------------------------------------------------------
// RUN THIS TEST FIRST
public void testConfirmSupport() throws Exception {
UDP_ACCESS = new DatagramSocket();
TCP_ACCESS = new ServerSocket(TCP_ACCESS_PORT);
LEAF[0] = new Connection("localhost", PORT);
LEAF[0].initialize(new LeafHeaders("localhost"), new EmptyResponder());
assertTrue("LEAF[0] should be connected", LEAF[0].isOpen());
// Give the connection a chance to send its initial messages
Thread.sleep( 1000*2 );
MessagesSupportedVendorMessage msvm =
(MessagesSupportedVendorMessage)getFirstMessageOfType(LEAF[0],
MessagesSupportedVendorMessage.class, 500);
assertNotNull(msvm);
assertGreaterThan(0, msvm.supportsTCPConnectBackRedirect());
assertGreaterThan(0, msvm.supportsUDPConnectBackRedirect());
}
public void testUDPConnectBackRedirect() throws Exception {
drainAll();
GUID cbGuid = new GUID(GUID.makeGuid());
UDPConnectBackRedirect udp =
new UDPConnectBackRedirect(cbGuid, InetAddress.getLocalHost(),
UDP_ACCESS.getLocalPort());
LEAF[0].send(udp);
LEAF[0].flush();
// we should NOT get a ping request over our UDP socket....
UDP_ACCESS.setSoTimeout(1000);
DatagramPacket pack = new DatagramPacket(new byte[1000], 1000);
try {
UDP_ACCESS.receive(pack);
fail("got UDP msg");
} catch (IOException good) {}
cbGuid = new GUID(GUID.makeGuid());
udp = new UDPConnectBackRedirect(cbGuid, InetAddress.getLocalHost(),
UDP_ACCESS.getLocalPort());
ULTRAPEER[0].send(udp);
ULTRAPEER[0].flush();
// we should get a ping reply over our UDP socket....
while (true) {
pack = new DatagramPacket(new byte[1000], 1000);
try {
UDP_ACCESS.receive(pack);
InputStream in = new ByteArrayInputStream(pack.getData());
Message m = Message.read(in);
if (m instanceof PingRequest) {
PingRequest reply = (PingRequest) m;
assertEquals(new GUID(reply.getGUID()), cbGuid);
break;
}
} catch (IOException bad) {
fail("got IOX", bad);
}
}
}
public void testUDPConnectBackAlreadyConnected() throws Exception {
drainAll();
DatagramSocket tempSock = new DatagramSocket(LEAF[0].getSocket().getLocalPort());
GUID cbGuid = new GUID(GUID.makeGuid());
UDPConnectBackRedirect udp =
new UDPConnectBackRedirect(cbGuid, LEAF[0].getSocket().getInetAddress(),
LEAF[0].getSocket().getLocalPort());
ULTRAPEER[0].send(udp);
ULTRAPEER[0].flush();
// we should NOT get a ping request over our UDP socket....
tempSock.setSoTimeout(1000);
DatagramPacket pack = new DatagramPacket(new byte[1000], 1000);
try {
tempSock.receive(pack);
fail("got UDP msg");
} catch (IOException good) {}
tempSock.close();
}
public void testTCPConnectBackRedirect() throws Exception {
drainAll();
TCPConnectBackRedirect tcp =
new TCPConnectBackRedirect(InetAddress.getLocalHost(),
TCP_ACCESS.getLocalPort());
LEAF[0].send(tcp);
LEAF[0].flush();
// we should NOT get a incoming connection
TCP_ACCESS.setSoTimeout(1000);
try {
TCP_ACCESS.accept();
fail("got IOX");
} catch (IOException good) {}
tcp = new TCPConnectBackRedirect(InetAddress.getLocalHost(),
TCP_ACCESS.getLocalPort());
ULTRAPEER[0].send(tcp);
ULTRAPEER[0].flush();
// we should get a incoming connection
try {
Socket x = TCP_ACCESS.accept();
// just like Acceptor reads words.
String word = IOUtils.readLargestWord(x.getInputStream(), 8);
assertEquals("CONNECT", word);
} catch (IOException bad) {
fail("got IOX", bad);
}
}
public void testTCPConnectBackAlreadyConnected() throws Exception {
drainAll();
TCPConnectBackRedirect tcp =
new TCPConnectBackRedirect(LEAF[0].getSocket().getInetAddress(),
TCP_ACCESS.getLocalPort());
ULTRAPEER[0].send(tcp);
ULTRAPEER[0].flush();
// we should NOT get an incoming connection
TCP_ACCESS.setSoTimeout(1000);
try {
TCP_ACCESS.accept();
fail("got TCP connection");
} catch (IOException good) {}
}
public void testConnectBackExpirer() throws Exception {
drainAll();
GUID cbGuid = new GUID(GUID.makeGuid());
UDPConnectBackRedirect udp =
new UDPConnectBackRedirect(cbGuid, InetAddress.getLocalHost(),
UDP_ACCESS.getLocalPort());
ULTRAPEER[1].send(udp);
ULTRAPEER[1].flush();
// we should NOT get a ping request over our UDP because we just did this
UDP_ACCESS.setSoTimeout(1000);
DatagramPacket pack = new DatagramPacket(new byte[1000], 1000);
try {
UDP_ACCESS.receive(pack);
fail("got UDP msg");
} catch (IOException good) {}
TCPConnectBackRedirect tcp =
new TCPConnectBackRedirect(InetAddress.getLocalHost(),
TCP_ACCESS.getLocalPort());
ULTRAPEER[1].send(tcp);
ULTRAPEER[1].flush();
// we should NOT get a incoming connection since we did this already
TCP_ACCESS.setSoTimeout(1000);
try {
TCP_ACCESS.accept();
fail("got TCP connection");
} catch (IOException good) {}
// simulate the running of the thread - technically i'm not testing
// the situation precisely, but i'm confident the schedule work so the
// abstraction isn't terrible
Thread cbThread = new Thread(new MessageRouter.ConnectBackExpirer());
cbThread.start();
cbThread.join();
// now these two things should work....
cbGuid = new GUID(GUID.makeGuid());
udp = new UDPConnectBackRedirect(cbGuid, InetAddress.getLocalHost(),
UDP_ACCESS.getLocalPort());
ULTRAPEER[1].send(udp);
ULTRAPEER[1].flush();
// we should get a ping request over our UDP
UDP_ACCESS.setSoTimeout(1000);
// we should get a ping reply over our UDP socket....
while (true) {
pack = new DatagramPacket(new byte[1000], 1000);
try {
UDP_ACCESS.receive(pack);
InputStream in = new ByteArrayInputStream(pack.getData());
Message m = Message.read(in);
if (m instanceof PingRequest) {
PingRequest reply = (PingRequest) m;
assertEquals(new GUID(reply.getGUID()), cbGuid);
break;
}
} catch (IOException bad) {
fail("got IOX", bad);
}
}
tcp = new TCPConnectBackRedirect(InetAddress.getLocalHost(),
TCP_ACCESS.getLocalPort());
ULTRAPEER[1].send(tcp);
ULTRAPEER[1].flush();
// we should get a incoming connection
TCP_ACCESS.setSoTimeout(1000);
try {
Socket x = TCP_ACCESS.accept();
byte[] read = new byte["CONNECT BACK\r\n\r\n".length() + 1];
int n = x.getInputStream().read(read);
assertEquals(read.length - 1, n);
assertEquals("CONNECT BACK\r\n\r\n\u0000".getBytes(), read);
} catch (IOException bad) {
fail("got IOX", bad);
}
}
// ------------------------------------------------------
}