package com.limegroup.gnutella;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Set;
import java.util.TreeSet;
import junit.framework.Test;
import org.limewire.concurrent.ThreadExecutor;
import org.limewire.core.settings.ConnectionSettings;
import org.limewire.core.settings.SpeedConstants;
import org.limewire.io.ConnectableImpl;
import org.limewire.io.GUID;
import org.limewire.io.IpPort;
import org.limewire.io.IpPortImpl;
import org.limewire.net.ConnectionDispatcher;
import org.limewire.net.TLSManager;
import org.limewire.nio.NIOSocket;
import com.google.inject.AbstractModule;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Stage;
import com.google.inject.name.Names;
import com.limegroup.gnutella.downloader.RemoteFileDescFactory;
import com.limegroup.gnutella.messages.MessageFactory;
import com.limegroup.gnutella.messages.PushRequest;
import com.limegroup.gnutella.messages.Message.Network;
import com.limegroup.gnutella.stubs.AcceptorStub;
import com.limegroup.gnutella.util.LimeTestCase;
/**
* Tests the issuing of Push Request through udp and failover to tcp.
*/
public class UDPPushTest extends LimeTestCase {
private final static byte[] guid = GUID.fromHexString("BC1F6870696111D4A74D0001031AE043");
private RemoteFileDesc rfd1, rfd2, rfdAlt;
/**
* the socket that will supposedly be the push download
*/
private Socket socket;
/**
* the socket that will listen for the tcp push request
*/
private ServerSocket serversocket;
/**
* the socket that will listen for the udp push request
*/
private DatagramSocket udpsocket;
private MessageFactory messageFactory;
private ConnectionDispatcher connectionDispatcher;
private Injector injector;
private TLSManager tlsManager;
private PushEndpointFactory pushEndpointFactory;
public UDPPushTest(String name) {
super(name);
}
public static Test suite() {
return buildTestSuite(UDPPushTest.class);
}
@Override
public void setUp() throws Exception {
ConnectionSettings.LOCAL_IS_PRIVATE.setValue(false);
ConnectionSettings.SOLICITED_GRACE_PERIOD.setValue(5000l);
// initialize services
injector = LimeTestUtils.createInjector(Stage.PRODUCTION, new AbstractModule() {
@Override
protected void configure() {
bind(Acceptor.class).to(AcceptorStub.class);
}
});
AcceptorStub acceptor = (AcceptorStub) injector.getInstance(Acceptor.class);
acceptor.setAcceptedIncoming(true);
acceptor.setAddress(InetAddress.getLocalHost());
serversocket = new ServerSocket(10000);
serversocket.setSoTimeout(1000);
udpsocket = new DatagramSocket(20000);
udpsocket.setSoTimeout(1000);
messageFactory = injector.getInstance(MessageFactory.class);
connectionDispatcher = injector.getInstance(Key.get(ConnectionDispatcher.class, Names.named("global")));
pushEndpointFactory = injector.getInstance(PushEndpointFactory.class);
tlsManager = injector.getInstance(TLSManager.class);
// initialize test data
long now = System.currentTimeMillis();
Set<IpPortImpl> proxies = new TreeSet<IpPortImpl>(IpPort.COMPARATOR);
proxies.add(new IpPortImpl(InetAddress.getLocalHost().getHostAddress(), 10000));
PushEndpoint pushEndpoint = pushEndpointFactory.createPushEndpoint(guid, proxies, PushEndpoint.PPTLS_BINARY, 0, new ConnectableImpl("127.0.0.1", 20000, true));
rfd1 = injector.getInstance(RemoteFileDescFactory.class).createRemoteFileDesc(pushEndpoint, 30l, "file1", 100, guid,
SpeedConstants.CABLE_SPEED_INT, 1, false, null, URN.NO_URN_SET, false, "LIME", now);
rfd2 = injector.getInstance(RemoteFileDescFactory.class).createRemoteFileDesc(pushEndpoint, 31l, "file2", 100, guid,
SpeedConstants.CABLE_SPEED_INT, 1, false, null, URN.NO_URN_SET, false, "LIME", now);
rfdAlt = injector.getInstance(RemoteFileDescFactory.class).createRemoteFileDesc(pushEndpoint, 30l, "file1", 100, guid,
SpeedConstants.CABLE_SPEED_INT, 1, false, null, URN.NO_URN_SET, false, "ALT", now);
injector.getInstance(LifecycleManager.class).start();
}
@Override
protected void tearDown() throws Exception {
injector.getInstance(LifecycleManager.class).shutdown();
serversocket.close();
udpsocket.close();
}
/**
* tests the scenario where an udp push is sent, but no connection is
* received so the failover tcp push is sent.
*/
public void testUDPPushFailover() throws Exception {
requestPush(rfd1);
try {
serversocket.accept();
fail("tcp attempt was made");
} catch (IOException expected) {
}
DatagramPacket push = new DatagramPacket(new byte[1000], 1000);
udpsocket.receive(push);
ByteArrayInputStream bais = new ByteArrayInputStream(push.getData());
PushRequest pr = (PushRequest)messageFactory.read(bais, Network.TCP);
assertEquals(rfd1.getIndex(), pr.getIndex());
Thread.sleep(5200);
Socket s = serversocket.accept();
assertTrue(s.isConnected());
s.close();
}
/**
* tests the scenario where an udp push is sent, no connection is received
* but since we're trying to contact an altloc no failover tcp push is sent.
*/
public void testUDPPushFailoverAlt() throws Exception {
requestPush(rfdAlt);
try {
serversocket.accept();
fail("tcp attempt was made");
} catch (IOException expected) {
}
DatagramPacket push = new DatagramPacket(new byte[1000], 1000);
udpsocket.receive(push);
ByteArrayInputStream bais = new ByteArrayInputStream(push.getData());
PushRequest pr = (PushRequest) messageFactory.read(bais, Network.TCP);
assertEquals(rfd1.getIndex(), pr.getIndex());
Thread.sleep(5200);
try {
Socket s = serversocket.accept();
s.close();
fail("tcp attempt was made");
} catch (IOException expected) {
}
Thread.sleep(3000);
}
/**
* tests the scenario where an UDP push is sent and a connection is
* established, so no failover occurs.
*/
public void testUDPPush() throws Exception {
requestPush(rfd1);
try {
serversocket.accept();
fail("tcp attempt was made");
} catch (IOException expected) {
}
DatagramPacket push = new DatagramPacket(new byte[1000], 1000);
udpsocket.receive(push);
ByteArrayInputStream bais = new ByteArrayInputStream(push.getData());
PushRequest pr = (PushRequest) messageFactory.read(bais, Network.TCP);
assertEquals(rfd1.getIndex(), pr.getIndex());
socket = new NIOSocket(InetAddress.getLocalHost(), 10000);
Socket other = serversocket.accept();
assertEquals(InetAddress.getLocalHost(), socket.getInetAddress());
assertEquals(10000, socket.getPort());
sendGiv(other, "0:BC1F6870696111D4A74D0001031AE043/file1\n\n");
connectionDispatcher.dispatch("GIV", socket, false);
Thread.sleep(1000);
other.close();
Thread.sleep(5000);
try {
Socket s = serversocket.accept();
s.close();
fail("tcp attempt was made");
} catch (IOException expected) {
}
Thread.sleep(3000);
}
/**
* tests the scenario where two pushes are made to the same host for
* different files and both succeed.
*/
public void testTwoPushesBothGood() throws Exception {
requestPush(rfd1);
requestPush(rfd2);
try {
serversocket.accept();
fail("tcp attempt was made");
} catch (IOException expected) {
}
DatagramPacket push = new DatagramPacket(new byte[1000], 1000);
udpsocket.receive(push);
ByteArrayInputStream bais = new ByteArrayInputStream(push.getData());
PushRequest pr = (PushRequest) messageFactory.read(bais, Network.TCP);
assertEquals(rfd1.getIndex(), pr.getIndex());
udpsocket.receive(push);
bais = new ByteArrayInputStream(push.getData());
pr = (PushRequest) messageFactory.read(bais, Network.TCP);
assertEquals(rfd2.getIndex(), pr.getIndex());
Thread.sleep(2000);
socket = new NIOSocket(InetAddress.getLocalHost(), 10000);
Socket other = serversocket.accept();
sendGiv(other, "0:BC1F6870696111D4A74D0001031AE043/file1\n\n");
connectionDispatcher.dispatch("GIV", socket, false);
Thread.sleep(1000);
socket.close();
socket = new NIOSocket(InetAddress.getLocalHost(), 10000);
other = serversocket.accept();
socket.setSoTimeout(1000);
sendGiv(other, "0:BC1F6870696111D4A74D0001031AE043/file2\n\n");
connectionDispatcher.dispatch("GIV", socket, false);
Thread.sleep(1000);
socket.close();
Thread.sleep(5200);
try {
serversocket.accept();
fail("tcp attempt was made");
} catch (IOException expected) {
}
}
public void testTwoPushesOneFails() throws Exception {
requestPush(rfd1);
requestPush(rfd2);
try {
serversocket.accept();
fail("tcp attempt was made");
} catch (IOException expected) {
}
DatagramPacket push = new DatagramPacket(new byte[1000], 1000);
udpsocket.receive(push);
ByteArrayInputStream bais = new ByteArrayInputStream(push.getData());
PushRequest pr = (PushRequest) messageFactory.read(bais, Network.TCP);
assertEquals(rfd1.getIndex(), pr.getIndex());
udpsocket.receive(push);
bais = new ByteArrayInputStream(push.getData());
pr = (PushRequest) messageFactory.read(bais, Network.TCP);
assertEquals(rfd2.getIndex(), pr.getIndex());
Thread.sleep(2000);
socket = new NIOSocket(InetAddress.getLocalHost(), 10000);
Socket other = serversocket.accept();
sendGiv(other, "0:BC1F6870696111D4A74D0001031AE043/file1\n\n");
connectionDispatcher.dispatch("GIV", socket, false);
Thread.sleep(1000);
socket.close();
Thread.sleep(5200);
serversocket.accept().close();
}
/**
* tests the scenario where two pushes are made to the same host for
* different files and both succeed.
*/
public void testPushContainsTLS() throws Exception {
tlsManager.setIncomingTLSEnabled(false);
requestPush(rfd1);
tlsManager.setIncomingTLSEnabled(true);
requestPush(rfd2);
try {
serversocket.accept();
fail("tcp attempt was made");
} catch (IOException expected) {
}
DatagramPacket push = new DatagramPacket(new byte[1000], 1000);
udpsocket.receive(push);
ByteArrayInputStream bais = new ByteArrayInputStream(push.getData());
PushRequest pr = (PushRequest) messageFactory.read(bais, Network.TCP);
assertEquals(rfd1.getIndex(), pr.getIndex());
assertFalse(pr.isTLSCapable());
udpsocket.receive(push);
bais = new ByteArrayInputStream(push.getData());
pr = (PushRequest) messageFactory.read(bais, Network.TCP);
assertEquals(rfd2.getIndex(), pr.getIndex());
assertTrue(pr.isTLSCapable());
// Finish off the test, just to make sure the failover doesn't kick in
// later.
Thread.sleep(2000);
socket = new NIOSocket(InetAddress.getLocalHost(), 10000);
Socket other = serversocket.accept();
sendGiv(other, "0:BC1F6870696111D4A74D0001031AE043/file1\n\n");
connectionDispatcher.dispatch("GIV", socket, false);
Thread.sleep(1000);
socket.close();
socket = new NIOSocket(InetAddress.getLocalHost(), 10000);
other = serversocket.accept();
socket.setSoTimeout(1000);
sendGiv(other, "0:BC1F6870696111D4A74D0001031AE043/file2\n\n");
connectionDispatcher.dispatch("GIV", socket, false);
Thread.sleep(1000);
socket.close();
Thread.sleep(5200);
try {
serversocket.accept();
fail("tcp attempt was made");
} catch (IOException expected) {
}
}
private void requestPush(final RemoteFileDesc rfd) throws Exception {
// Thread t = ThreadExecutor.newManagedThread(new Runnable() {
// public void run() {
// injector.getInstance(DownloadManager.class).getPushManager().sendPush(rfd);
// }
// });
// t.start();
// Thread.sleep(100);
//
((DownloadManagerImpl)injector.getInstance(DownloadManager.class)).getPushManager().sendPush(rfd);
}
private void sendGiv(final Socket sock, final String str) {
Thread t = ThreadExecutor.newManagedThread(new Runnable() {
public void run() {
try {
sock.getOutputStream().write(str.getBytes());
} catch (IOException e) {
fail(e);
}
}
});
t.start();
}
}