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.Set;
import java.util.StringTokenizer;
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.search.HostData;
import com.limegroup.gnutella.stubs.ActivityCallbackStub;
import com.limegroup.gnutella.util.IpPort;
import com.limegroup.gnutella.util.IpPortImpl;
import com.limegroup.gnutella.util.IpPortSet;
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 ClientSideBrowseHostTest extends ClientSideTestCase {
private MyActivityCallback callback;
public ClientSideBrowseHostTest(String name) {
super(name);
}
public static Test suite() {
return buildTestSuite(ClientSideBrowseHostTest.class);
}
public static void main(String[] args) {
junit.textui.TestRunner.run(suite());
}
///////////////////////// Actual Tests ////////////////////////////
// Tests the following behaviors:
// ------------------------------
// 1. that the client makes a correct direct connection if possible
// 2. that the client makes a correct push proxy connection if necessary
// 3. if all else fails the client sends a PushRequest
public static void globalSetUp() throws Exception {
PrivilegedAccessor.setValue(RouterService.getAcceptor(),"_acceptedIncoming", Boolean.TRUE);
}
public void testHTTPRequest() throws Exception {
callback = (MyActivityCallback) getCallback();
drain(testUP[0]);
// some setup
final 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(TIMEOUT);
// send a reply with some PushProxy info
IpPort[] proxies = new IpPortImpl[1];
proxies[0] = 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, 7000,
InetAddress.getLocalHost().getAddress(), 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);
assertNotNull(callback.getRFD());
// tell the leaf to browse host the file, should result in direct HTTP
// request
RouterService.doAsynchronousBrowseHost(callback.getRFD().getHost(),
callback.getRFD().getPort(),
new GUID(GUID.makeGuid()), new GUID(clientGUID),
null, false);
// 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 / HTTP/1.1"));
// make sure the node sends the correct Host val
currLine = reader.readLine();
assertTrue(currLine.startsWith("Host:"));
StringTokenizer st = new StringTokenizer(currLine, ":");
assertEquals(st.nextToken(), "Host");
InetAddress addr = InetAddress.getByName(st.nextToken().trim());
Arrays.equals(addr.getAddress(), RouterService.getAddress());
assertEquals(Integer.parseInt(st.nextToken()), SERVER_PORT);
// let the other side do its thing
Thread.sleep(500);
// send back a 200 and make sure no PushRequest is sent via the normal
// way
BufferedWriter writer =
new BufferedWriter(new
OutputStreamWriter(httpSock.getOutputStream()));
writer.write("HTTP/1.1 200 OK\r\n");
writer.flush();
writer.write("\r\n");
writer.flush();
//TODO: should i send some Query Hits? Might be a good test.
httpSock.close();
try {
do {
m = testUP[0].receive(TIMEOUT);
assertTrue(!(m instanceof PushRequest));
} while (true) ;
}
catch (InterruptedIOException expected) {}
// awesome - everything checks out!
ss.close();
}
public void testPushProxyRequest() throws Exception {
Thread.sleep(6000);
callback = (MyActivityCallback) getCallback();
drain(testUP[0]);
// some setup
final byte[] clientGUID = GUID.makeGuid();
// construct and send a query
byte[] guid = GUID.makeGuid();
RouterService.query(guid, "nyu.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 to wait for proxy request
ServerSocket ss = new ServerSocket(7000);
ss.setReuseAddress(true);
ss.setSoTimeout(TIMEOUT*4);
// send a reply with some PushProxy info
//final PushProxyInterface[] proxies =
// new QueryReply.PushProxyContainer[1];
final Set proxies = new IpPortSet();
proxies.add(new IpPortImpl("127.0.0.1", 7000));
Response[] res = new Response[1];
res[0] = new Response(10, 10, "nyu.edu");
m = new QueryReply(m.getGUID(), (byte) 1, 6999,
InetAddress.getLocalHost().getAddress(), 0, res,
clientGUID, new byte[0], false, 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(callback.getRFD() != null);
// tell the leaf to browse host the file, should result in PushProxy
// request
RouterService.doAsynchronousBrowseHost(callback.getRFD().getHost(),
callback.getRFD().getPort(),
new GUID(GUID.makeGuid()), new GUID(clientGUID),
proxies, false);
// wait for the incoming PushProxy request
// increase the timeout since we send udp pushes first
ss.setSoTimeout(7000);
Socket httpSock = ss.accept();
assertNotNull(httpSock);
BufferedWriter sockWriter =
new BufferedWriter(new
OutputStreamWriter(httpSock.getOutputStream()));
sockWriter.write("HTTP/1.1 202 OK\r\n");
sockWriter.flush();
// 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()), SERVER_PORT);
// now we need to GIV
Socket push = new Socket(InetAddress.getLocalHost(), SERVER_PORT);
BufferedWriter writer =
new BufferedWriter(new
OutputStreamWriter(push.getOutputStream()));
writer.write("GIV 0:" + new GUID(clientGUID).toHexString() + "/\r\n");
writer.write("\r\n");
writer.flush();
// confirm a BrowseHost request
reader =
new BufferedReader(new
InputStreamReader(push.getInputStream()));
currLine = reader.readLine();
assertTrue(currLine.startsWith("GET / HTTP/1.1"));
// make sure the node sends the correct Host val
currLine = reader.readLine();
assertTrue(currLine.startsWith("Host:"));
st = new StringTokenizer(currLine, ":");
assertEquals(st.nextToken(), "Host");
addr = InetAddress.getByName(st.nextToken().trim());
Arrays.equals(addr.getAddress(), RouterService.getAddress());
assertEquals(Integer.parseInt(st.nextToken()), SERVER_PORT);
// let the other side do its thing
Thread.sleep(500);
// send back a 200 and make sure no PushRequest is sent via the normal
// way
writer =
new BufferedWriter(new
OutputStreamWriter(push.getOutputStream()));
writer.write("HTTP/1.1 200 OK\r\n");
writer.flush();
writer.write("\r\n");
writer.flush();
httpSock.close();
try {
do {
m = testUP[0].receive(TIMEOUT);
assertNotInstanceof(m.toString(), PushRequest.class, m);
} while (true) ;
}
catch (InterruptedIOException expected) {}
// awesome - everything checks out!
ss.close();
}
public void testSendsPushRequest() throws Exception {
callback = (MyActivityCallback) getCallback();
drain(testUP[0]);
// some setup
final byte[] clientGUID = GUID.makeGuid();
// construct and send a query
byte[] guid = GUID.makeGuid();
RouterService.query(guid, "anita");
// the testUP[0] should get it
Message m = null;
do {
m = testUP[0].receive(TIMEOUT);
} while (!(m instanceof QueryRequest)) ;
// send a reply with some BAD PushProxy info
//final PushProxyInterface[] proxies =
// new QueryReply.PushProxyContainer[1];
final Set proxies = new IpPortSet();
proxies.add(new IpPortImpl("127.0.0.1", 7001));
Response[] res = new Response[1];
res[0] = new Response(10, 10, "anita");
m = new QueryReply(m.getGUID(), (byte) 1, 7000,
InetAddress.getLocalHost().getAddress(), 0, res,
clientGUID, new byte[0], false, 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(callback.getRFD() != null);
// tell the leaf to browse host the file,
RouterService.doAsynchronousBrowseHost(callback.getRFD().getHost(),
callback.getRFD().getPort(),
new GUID(GUID.makeGuid()), new GUID(clientGUID),
proxies, false);
// nothing works for the guy, we should get a PushRequest
do {
m = testUP[0].receive(TIMEOUT*30);
} while (!(m instanceof PushRequest));
// awesome - everything checks out!
}
//////////////////////////////////////////////////////////////////
public static Integer numUPs() {
return new Integer(1);
}
public static ActivityCallback getActivityCallback() {
return new MyActivityCallback();
}
private static class MyActivityCallback extends ActivityCallbackStub {
private RemoteFileDesc _rfd = null;
public RemoteFileDesc getRFD() {
return _rfd;
}
public void handleQueryResult(RemoteFileDesc rfd,
HostData data,
Set locs) {
_rfd = rfd;
}
}
}