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.Socket;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import junit.framework.Test;
import org.apache.http.protocol.HTTP;
import org.limewire.gnutella.tests.ActivityCallbackStub;
import org.limewire.io.GUID;
import org.limewire.io.NetworkUtils;
import org.limewire.util.Base32;
import com.limegroup.gnutella.connection.RoutedConnection;
import com.limegroup.gnutella.helpers.UrnHelper;
import com.limegroup.gnutella.messages.Message;
import com.limegroup.gnutella.messages.PushRequest;
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.routing.QueryRouteTable;
import com.limegroup.gnutella.routing.RouteTableMessage;
import com.limegroup.gnutella.util.EmptyResponder;
/**
* Tests that an Ultrapeer correctly handles all aspects of PushProxy. For
* example:
* 1) handles the VendorMessage exchange as expected
* 2) handles HTTP requests as expected, forwarding on a PushRequest
*
* This class tests a lot of different pieces of code.
*
* ULTRAPEER_1 ---- CENTRAL TEST ULTRAPEER ---- ULTRAPEER_2
* |
* |
* |
* LEAF
*
* The leaf must be connected in the first test.
*/
@SuppressWarnings("unchecked")
public final class ServerSidePushProxyTest extends ServerSideTestCase {
/**
* The timeout value for sockets -- how much time we wait to accept
* individual messages before giving up.
*/
private static final int TIMEOUT = 2000;
/**
* the client guid of the LEAF - please set in the first test.
*/
private byte[] leafGUIDBytes = null;
/**
* the client GUID of the leaf as a GUID.
*/
private GUID leafGUID = null;
public ServerSidePushProxyTest(String name) {
super(name);
}
public static Test suite() {
return buildTestSuite(ServerSidePushProxyTest.class);
}
public static void main(String[] args) {
junit.textui.TestRunner.run(suite());
}
@Override
public int getNumberOfUltrapeers() {
return 1;
}
@Override
public int getNumberOfLeafpeers() {
return 1;
}
public static ActivityCallback getActivityCallback() {
return new ActivityCallbackStub();
}
@Override
public void setUpQRPTables() throws Exception {
// for Ultrapeer 1
QueryRouteTable qrt = new QueryRouteTable();
qrt.add("leehsus");
qrt.add("berkeley");
for (Iterator iter=qrt.encode(null).iterator(); iter.hasNext(); ) {
ULTRAPEER[0].send((RouteTableMessage)iter.next());
ULTRAPEER[0].flush();
}
}
// BEGIN TESTS
// ------------------------------------------------------
@Override
protected void setUp() throws Exception {
super.setUp();
drainAll();
Message m = null;
leafGUIDBytes = GUID.makeGuid();
leafGUID = new GUID(leafGUIDBytes);
LEAF[0] = blockingConnectionFactory.createConnection("localhost", PORT);
// routed leaf, with route table for "test"
LEAF[0].initialize(headersFactory.createLeafHeaders("localhost"), new EmptyResponder(), 1000);
QueryRouteTable qrt = new QueryRouteTable();
qrt.add("berkeley");
qrt.add("susheel");
qrt.addIndivisible(UrnHelper.UNIQUE_SHA1.toString());
for (Iterator iter=qrt.encode(null).iterator(); iter.hasNext(); ) {
LEAF[0].send((RouteTableMessage)iter.next());
LEAF[0].flush();
}
// make sure UP is advertised proxy support
do {
m = LEAF[0].receive(TIMEOUT);
} while (!(m instanceof MessagesSupportedVendorMessage)) ;
assertTrue(((MessagesSupportedVendorMessage)m).supportsPushProxy() > 0);
// send proxy request
PushProxyRequest req = new PushProxyRequest(new GUID(leafGUIDBytes));
LEAF[0].send(req);
LEAF[0].flush();
// wait for ack
do {
m = LEAF[0].receive(TIMEOUT);
} while (!(m instanceof PushProxyAcknowledgement)) ;
assertTrue(Arrays.equals(m.getGUID(), leafGUIDBytes));
assertEquals(PORT, ((PushProxyAcknowledgement)m).getListeningPort());
// ultrapeer supports push proxy setup A-OK
}
/**
* Integration test to make sure the client guid is set correctly on
* the connection when the push request is processed.
*/
public void testClientGuidOfProxiedLeafIsKnown() {
assertServerHasClientConnectionWithGuid(leafGUID);
}
public void testGETWithServerId() throws Exception {
makeHttpPushRequest("GET", // request method.
"/gnutella/push-proxy", // the request
"ServerID", // initial param
Base32.encode(leafGUIDBytes), // initial value
"127.0.0.1", // ip
6346, // port
null, // params
202); // opcode expected in return
}
public void testGETWithServerIdTLS() throws Exception {
Map m = new HashMap();
m.put("tls", "true");
makeHttpPushRequest("GET", // request method.
"/gnutella/push-proxy", // the request
"ServerID", // initial param
Base32.encode(leafGUIDBytes), // initial value
"127.0.0.1", // ip
6346, // port
m, // params
202); // opcode expected in return
}
public void testGETWithServerIdTLSFalse() throws Exception {
Map m = new HashMap();
m.put("tls", "false");
makeHttpPushRequest("GET", // request method.
"/gnutella/push-proxy", // the request
"ServerID", // initial param
Base32.encode(leafGUIDBytes), // initial value
"127.0.0.1", // ip
6346, // port
m, // params
202); // opcode expected in return
}
public void testHEADWithServerId() throws Exception {
makeHttpPushRequest("HEAD", "/gnutella/push-proxy", "Serverid",
Base32.encode(leafGUIDBytes), "10.238.1.87", 6350, null, 202);
}
public void testHEADWithServerIdTLS() throws Exception {
Map m = new HashMap();
m.put("tls", "true");
makeHttpPushRequest("HEAD", "/gnutella/push-proxy", "Serverid",
Base32.encode(leafGUIDBytes), "10.238.1.87", 6350, m, 202);
}
public void testHEADWithServerIdTLSFalse() throws Exception {
Map m = new HashMap();
m.put("tls", "false");
makeHttpPushRequest("HEAD", "/gnutella/push-proxy", "Serverid",
Base32.encode(leafGUIDBytes), "10.238.1.87", 6350, m, 202);
}
public void testHEADWithServerIdTLSOther() throws Exception {
Map m = new HashMap();
m.put("tls", "asdfa32");
makeHttpPushRequest("HEAD", "/gnutella/push-proxy", "Serverid",
Base32.encode(leafGUIDBytes), "10.238.1.87", 6350, m, 202);
}
public void testInvalidGUIDWithServerId() throws Exception {
makeHttpPushRequest("GET", "/gnutella/push-proxy", "serverid",
Base32.encode(GUID.makeGuid()), "127.0.0.1", 6346, null, 410);
}
public void testInvalidGUIDWithGuid() throws Exception {
makeHttpPushRequest("GET", "/gnutella/push-proxy", "guid",
new GUID().toHexString(), "127.0.0.1", 6346, null, 410);
}
public void testInvalidIP() throws Exception {
makeHttpPushRequest("GET", "/gnutella/push-proxy", "serverid",
Base32.encode(leafGUIDBytes), "www.crapalapadapa.com",
6346, null, 400);
}
public void testServerIdWithBase16Fails() throws Exception {
makeHttpPushRequest("GET", "/gnutella/push-proxy", "serverid",
leafGUID.toHexString(), "127.0.0.1", 6346, null, 400);
}
public void testGuidIsBase16() throws Exception {
makeHttpPushRequest("GET", "/gnutella/push-proxy", "guid",
leafGUID.toHexString(), "127.0.0.1", 6346, null, 202);
}
public void testGuidIsBase16TLS() throws Exception {
Map m = new HashMap();
m.put("tls", "true");
makeHttpPushRequest("GET", "/gnutella/push-proxy", "guid",
leafGUID.toHexString(), "127.0.0.1", 6346, m, 202);
}
public void testGuidIsBase16TLSFalse() throws Exception {
Map m = new HashMap();
m.put("tls", "false");
makeHttpPushRequest("GET", "/gnutella/push-proxy", "guid",
leafGUID.toHexString(), "127.0.0.1", 6346, m, 202);
}
public void testGuidWithBase32Fails() throws Exception {
makeHttpPushRequest("GET", "/gnutella/push-proxy", "guid",
Base32.encode(leafGUIDBytes), "127.0.0.1", 6346, null, 400);
}
public void testGuidWithGnet() throws Exception {
makeHttpPushRequest("GET", "/gnet/push-proxy", "guid",
Base32.encode(leafGUIDBytes), "127.0.0.1", 6346, null, 400);
}
public void testFileChangesIndex() throws Exception {
Map m = new HashMap();
m.put("file", new Integer(34));
makeHttpPushRequest("GET", "/gnutella/push-proxy", "guid",
leafGUID.toHexString(), "127.0.0.1", 6346, m, 202);
}
public void testFileChangesIndexTLS() throws Exception {
Map m = new HashMap();
m.put("file", new Integer(34));
m.put("tls", "true");
makeHttpPushRequest("GET", "/gnutella/push-proxy", "guid",
leafGUID.toHexString(), "127.0.0.1", 6346, m, 202);
}
public void testFileChangesIndexTLSFalse() throws Exception {
Map m = new HashMap();
m.put("file", new Integer(34));
m.put("tls", "false");
makeHttpPushRequest("GET", "/gnutella/push-proxy", "guid",
leafGUID.toHexString(), "127.0.0.1", 6346, m, 202);
}
public void testFileWithGnet() throws Exception {
Map m = new HashMap();
m.put("file", new Integer(34));
makeHttpPushRequest("GET", "/gnet/push-proxy", "guid",
leafGUID.toHexString(), "127.0.0.1", 6346, m, 202);
}
public void testCannotHaveServeridAndGuid() throws Exception {
Map m = new HashMap();
m.put("serverid", Base32.encode(leafGUIDBytes));
makeHttpPushRequest("GET", "/gnutella/push-proxy", "guid",
leafGUID.toHexString(), "127.0.0.1", 6346, m, 400);
}
public void testMultipleFileFails() throws Exception {
Map m = new HashMap();
m.put("FILE", new Integer(1));
m.put("file", new Integer(2));
makeHttpPushRequest("GET", "/gnutella/push-proxy", "guid",
leafGUID.toHexString(), "127.0.0.1", 6346, m, 400);
}
public void testFirewallTransferPushProxyWorks() throws Exception {
Map m = new HashMap();
m.put("file", new Integer((int)PushRequest.FW_TRANS_INDEX));
makeHttpPushRequest("GET", "/gnutella/push-proxy", "ServerID",
Base32.encode(leafGUIDBytes), "127.0.0.1", 6346, m, 202);
}
public void testFirewallTransferPushProxyWorksTLS() throws Exception {
Map m = new HashMap();
m.put("file", new Integer((int)PushRequest.FW_TRANS_INDEX));
m.put("tls", "true");
makeHttpPushRequest("GET", "/gnutella/push-proxy", "ServerID",
Base32.encode(leafGUIDBytes), "127.0.0.1", 6346, m, 202);
}
public void testFirewallTransferPushProxyWorksTLSFalse() throws Exception {
Map m = new HashMap();
m.put("file", new Integer((int)PushRequest.FW_TRANS_INDEX));
m.put("tls", "false");
makeHttpPushRequest("GET", "/gnutella/push-proxy", "ServerID",
Base32.encode(leafGUIDBytes), "127.0.0.1", 6346, m, 202);
}
private void assertServerHasClientConnectionWithGuid(GUID guid) {
ConnectionManager connectionManager = injector.getInstance(ConnectionManager.class);
for (RoutedConnection leaf : connectionManager.getInitializedClientConnections()) {
if (guid.equals(new GUID(leaf.getClientGUID()))) {
assertTrue(leaf.isPushProxyFor());
return;
}
}
fail("No leaf connection found with guid: " + guid);
}
private void makeHttpPushRequest(String reqMethod, String reqKey, String initKey,
String guid, String ip, int port, Map params,
int expectedHttpResponseCode)
throws Exception {
Socket s = new Socket("localhost", PORT);
BufferedReader in =
new BufferedReader(new InputStreamReader(s.getInputStream(), HTTP.DEFAULT_PROTOCOL_CHARSET));
BufferedWriter out =
new BufferedWriter(new OutputStreamWriter(s.getOutputStream(), HTTP.DEFAULT_PROTOCOL_CHARSET));
String result = null;
Message m = null;
out.write(reqMethod + " " + reqKey + "?");
out.write(initKey + "=" + guid);
if( params != null ) {
for(Iterator i = params.entrySet().iterator(); i.hasNext(); ) {
Map.Entry entry = (Map.Entry)i.next();
out.write("&" + entry.getKey() + "=" + entry.getValue());
}
}
out.write(" HTTP/1.1\r\n");
out.write("X-Node: ");
out.write(ip + ":" + port + "\r\n");
out.write("Connection: close\r\n");
out.write("\r\n");
out.flush();
// check opcode - less important, but might as well
result = in.readLine();
assertGreaterThan(result, -1, result.indexOf("" + expectedHttpResponseCode));
// clear out other responses
while (in.readLine() != null) ;
if( expectedHttpResponseCode != 202 ) {
// leaf NOT expecting PushRequest.
try {
do {
m = LEAF[0].receive(TIMEOUT);
assertTrue(!(m instanceof PushRequest));
} while (true) ;
}
catch (InterruptedIOException expected) {}
} else {
// leaf should get PushRequest
do {
m = LEAF[0].receive(TIMEOUT);
} while (!(m instanceof PushRequest)) ;
PushRequest pr = (PushRequest) m;
int idx = 0;
boolean tls = false;
if(params != null) {
if(params.get("file") != null )
idx = ((Integer)params.get("file")).intValue();
if(params.get("tls") != null && "true".equalsIgnoreCase((String)params.get("tls")))
tls = true;
}
assertEquals(idx, pr.getIndex());
assertEquals(new GUID(leafGUIDBytes), new GUID(pr.getClientGUID()));
assertEquals(port, pr.getPort());
assertEquals(ip, NetworkUtils.ip2string(pr.getIP()));
assertEquals(tls, pr.isTLSCapable());
}
}
}