package com.limegroup.gnutella;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import junit.framework.Test;
import com.bitzi.util.Base32;
import com.limegroup.gnutella.messages.Message;
import com.limegroup.gnutella.messages.PushRequest;
import com.limegroup.gnutella.messages.QueryReply;
import com.limegroup.gnutella.messages.QueryRequest;
import com.limegroup.gnutella.messages.vendor.MessagesSupportedVendorMessage;
import com.limegroup.gnutella.messages.vendor.PushProxyAcknowledgement;
import com.limegroup.gnutella.messages.vendor.PushProxyRequest;
import com.limegroup.gnutella.search.HostData;
import com.limegroup.gnutella.stubs.ActivityCallbackStub;
import com.limegroup.gnutella.util.CommonUtils;
import com.limegroup.gnutella.util.IpPort;
import com.limegroup.gnutella.util.IpPortImpl;
import com.limegroup.gnutella.util.PrivilegedAccessor;
/**
* Checks whether (multi)leaves avoid forwarding messages to ultrapeers, do
* redirects properly, etc. The test includes a leaf attached to 3
* Ultrapeers.
*/
public class ClientSidePushProxyTest extends ClientSideTestCase {
protected static final int PORT=6669;
protected static int TIMEOUT=1000; // should override super
public ClientSidePushProxyTest(String name) {
super(name);
}
public static Test suite() {
return buildTestSuite(ClientSidePushProxyTest.class);
}
public static void main(String[] args) {
junit.textui.TestRunner.run(suite());
}
private static void setAccepted(boolean yes) {
try {
PrivilegedAccessor.setValue(RouterService.getAcceptor(),"_acceptedIncoming",new Boolean(yes));
}catch(Exception bad) {
ErrorService.error(bad);
}
}
///////////////////////// Actual Tests ////////////////////////////
// THIS TEST SHOULD BE RUN FIRST!!
public void testPushProxySetup() throws Exception {
// send a MessagesSupportedMessage
testUP[0].send(MessagesSupportedVendorMessage.instance());
testUP[0].flush();
// we expect to get a PushProxy request
Message m = null;
do {
m = testUP[0].receive(TIMEOUT);
} while (!(m instanceof PushProxyRequest)) ;
// we should answer the push proxy request
PushProxyAcknowledgement ack =
new PushProxyAcknowledgement(InetAddress.getLocalHost(),
6355, new GUID(m.getGUID()));
testUP[0].send(ack);
testUP[0].flush();
// client side seems to follow the setup process A-OK
}
public void testQueryReplyHasProxiesAndCanGIV() throws Exception {
setAccepted(false);
drain(testUP[0]);
// make sure leaf is sharing
assertEquals(2, RouterService.getFileManager().getNumFiles());
// send a query that should be answered
QueryRequest query = new QueryRequest(GUID.makeGuid(), (byte) 1,
"berkeley", null, null, null,
null, false, 0, false, 0);
testUP[0].send(query);
testUP[0].flush();
// await a response
Message m = null;
do {
m = testUP[0].receive(TIMEOUT);
} while (!(m instanceof QueryReply)) ;
// confirm it has proxy info
QueryReply reply = (QueryReply) m;
assertNotNull(reply.getPushProxies());
// check out PushProxy info
Set proxies = reply.getPushProxies();
assertEquals(1, proxies.size());
Iterator iter = proxies.iterator();
IpPort ppi = (IpPort)iter.next();
assertEquals(ppi.getPort(), 6355);
assertTrue(ppi.getInetAddress().equals(testUP[0].getInetAddress()));
// set up a ServerSocket to get give on
ServerSocket ss = new ServerSocket(9000);
ss.setReuseAddress(true);
ss.setSoTimeout(TIMEOUT);
// test that the client responds to a PushRequest
PushRequest pr = new PushRequest(GUID.makeGuid(), (byte) 1,
RouterService.getMessageRouter()._clientGUID,
0,
InetAddress.getLocalHost().getAddress(),
9000);
// send the PR off
testUP[0].send(pr);
testUP[0].flush();
// we should get a incoming GIV
Socket givSock = ss.accept();
assertNotNull(givSock);
// start reading and confirming the HTTP request
String currLine = null;
BufferedReader reader =
new BufferedReader(new
InputStreamReader(givSock.getInputStream()));
// confirm a GIV
currLine = reader.readLine();
String givLine = "GIV 0:" +
(new GUID(RouterService.getMessageRouter()._clientGUID)).toHexString();
assertTrue(currLine.startsWith(givLine));
// everything checks out!
givSock.close();
ss.close();
}
public void testHTTPRequest() throws Exception {
setAccepted(true);
drain(testUP[0]);
// some setup
byte[] clientGUID = GUID.makeGuid();
// construct and send a query
byte[] guid = GUID.makeGuid();
RouterService.query(guid, "boalt.org");
// the testUP[0] should get it
Message m = null;
do {
m = testUP[0].receive(TIMEOUT);
} while (!(m instanceof QueryRequest)) ;
// set up a server socket
ServerSocket ss = new ServerSocket(7000);
ss.setReuseAddress(true);
ss.setSoTimeout(25*TIMEOUT);
// send a reply with some PushProxy info
Set proxies = new TreeSet(IpPort.COMPARATOR);
proxies.add(new IpPortImpl("127.0.0.1", 7000));
Response[] res = new Response[1];
res[0] = new Response(10, 10, "boalt.org");
m = new QueryReply(m.getGUID(), (byte) 1, 6355, myIP(), 0, res,
clientGUID, new byte[0], true, false, true,
true, false, false, proxies);
testUP[0].send(m);
testUP[0].flush();
// wait a while for Leaf to process result
Thread.sleep(2000);
assertTrue(((MyActivityCallback)getCallback()).getRFD() != null);
// tell the leaf to download the file, should result in push proxy
// request
RouterService.download((new RemoteFileDesc[]
{ ((MyActivityCallback)getCallback()).getRFD() }), true,
new GUID(m.getGUID()));
// wait for the incoming HTTP request
Socket httpSock = ss.accept();
assertNotNull(httpSock);
// start reading and confirming the HTTP request
String currLine = null;
BufferedReader reader =
new BufferedReader(new
InputStreamReader(httpSock.getInputStream()));
// confirm a GET/HEAD pushproxy request
currLine = reader.readLine();
assertTrue(currLine.startsWith("GET /gnutella/push-proxy") ||
currLine.startsWith("HEAD /gnutella/push-proxy"));
// make sure it sends the correct client GUID
int beginIndex = currLine.indexOf("ID=") + 3;
String guidString = currLine.substring(beginIndex, beginIndex+26);
GUID guidFromBackend = new GUID(clientGUID);
GUID guidFromNetwork = new GUID(Base32.decode(guidString));
assertEquals(guidFromNetwork, guidFromBackend);
// make sure the node sends the correct X-Node
currLine = reader.readLine();
assertTrue(currLine.startsWith("X-Node:"));
StringTokenizer st = new StringTokenizer(currLine, ":");
assertEquals(st.nextToken(), "X-Node");
InetAddress addr = InetAddress.getByName(st.nextToken().trim());
Arrays.equals(addr.getAddress(), RouterService.getAddress());
assertEquals(Integer.parseInt(st.nextToken()), PORT);
// send back a 202 and make sure no PushRequest is sent via the normal
// way
BufferedWriter writer =
new BufferedWriter(new
OutputStreamWriter(httpSock.getOutputStream()));
writer.write("HTTP/1.1 202 gobbledygook");
writer.flush();
httpSock.close();
try {
do {
m = testUP[0].receive(TIMEOUT);
assertTrue(!(m instanceof PushRequest));
} while (true) ;
}
catch (InterruptedIOException expected) {}
// now make a connection to the leaf to confirm that it will send a
// correct download request
Socket push = new Socket(InetAddress.getLocalHost(), PORT);
writer =
new BufferedWriter(new
OutputStreamWriter(push.getOutputStream()));
writer.write("GIV 0:" + new GUID(clientGUID).toHexString() + "/\r\n");
writer.write("\r\n");
writer.flush();
reader =
new BufferedReader(new
InputStreamReader(push.getInputStream()));
currLine = reader.readLine();
assertEquals("GET /get/10/boalt.org HTTP/1.1", currLine);
// awesome - everything checks out!
push.close();
ss.close();
}
public void testNoProxiesSendsPushNormal() throws Exception {
drain(testUP[0]);
// some setup
byte[] clientGUID = GUID.makeGuid();
// construct and send a query
byte[] guid = GUID.makeGuid();
RouterService.query(guid, "golf is awesome");
// the testUP[0] should get it
Message m = null;
do {
m = testUP[0].receive(TIMEOUT);
} while (!(m instanceof QueryRequest)) ;
// send a reply with NO PushProxy info
Response[] res = new Response[1];
res[0] = new Response(10, 10, "golf is awesome");
m = new QueryReply(m.getGUID(), (byte) 1, 6355, myIP(), 0, res,
clientGUID, new byte[0], false, false, true,
true, false, false, null);
testUP[0].send(m);
testUP[0].flush();
// wait a while for Leaf to process result
Thread.sleep(1000);
assertTrue(((MyActivityCallback)getCallback()).getRFD() != null);
// tell the leaf to download the file, should result in normal TCP
// PushRequest
RouterService.download((new RemoteFileDesc[]
{ ((MyActivityCallback)getCallback()).getRFD() }), true,
new GUID(m.getGUID()));
// await a PushRequest
do {
m = testUP[0].receive(25*TIMEOUT);
} while (!(m instanceof PushRequest)) ;
}
public void testCanReactToBadPushProxy() throws Exception {
drain(testUP[0]);
// some setup
byte[] clientGUID = GUID.makeGuid();
// construct and send a query
byte[] guid = GUID.makeGuid();
RouterService.query(guid, "berkeley.edu");
// the testUP[0] should get it
Message m = null;
do {
m = testUP[0].receive(TIMEOUT);
} while (!(m instanceof QueryRequest)) ;
// set up a server socket
ServerSocket ss = new ServerSocket(7000);
ss.setReuseAddress(true);
ss.setSoTimeout(25*TIMEOUT);
// send a reply with some BAD PushProxy info
//PushProxyInterface[] proxies = new QueryReply.PushProxyContainer[2];
Set proxies = new TreeSet(IpPort.COMPARATOR);
proxies.add(new IpPortImpl("127.0.0.1", 7000));
proxies.add(new IpPortImpl("127.0.0.1", 8000));
Response[] res = new Response[1];
res[0] = new Response(10, 10, "berkeley.edu");
m = new QueryReply(m.getGUID(), (byte) 1, 6355, myIP(), 0, res,
clientGUID, new byte[0], true, false, true,
true, false, false, proxies);
testUP[0].send(m);
testUP[0].flush();
// wait a while for Leaf to process result
Thread.sleep(1000);
assertTrue(((MyActivityCallback)getCallback()).getRFD() != null);
// tell the leaf to download the file, should result in push proxy
// request
RouterService.download((new RemoteFileDesc[]
{ ((MyActivityCallback)getCallback()).getRFD() }), true,
new GUID((m.getGUID())));
// wait for the incoming HTTP request
Socket httpSock = ss.accept();
assertNotNull(httpSock);
// send back a error and make sure the PushRequest is sent via the normal
// way
BufferedWriter writer =
new BufferedWriter(new
OutputStreamWriter(httpSock.getOutputStream()));
writer.write("HTTP/1.1 410 gobbledygook");
writer.flush();
// there is something going on with timeouts here....
if (CommonUtils.isMacOSX() || CommonUtils.isWindows())
Thread.sleep(300);
httpSock.close();
// await a PushRequest
do {
m = testUP[0].receive(TIMEOUT*8);
} while (!(m instanceof PushRequest)) ;
// everything checks out
ss.close();
}
//////////////////////////////////////////////////////////////////
public static Integer numUPs() {
return new Integer(1);
}
public static ActivityCallback getActivityCallback() {
return new MyActivityCallback();
}
private static byte[] myIP() {
return new byte[] { (byte)192, (byte)168, 0, 1 };
}
public static class MyActivityCallback extends ActivityCallbackStub {
private RemoteFileDesc rfd = null;
public RemoteFileDesc getRFD() {
return rfd;
}
public void handleQueryResult(RemoteFileDesc rfd,
HostData data,
Set locs) {
this.rfd = rfd;
}
}
}