package com.limegroup.gnutella; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.Properties; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import junit.framework.Test; import org.limewire.core.settings.ApplicationSettings; import org.limewire.core.settings.ConnectionSettings; import org.limewire.core.settings.NetworkSettings; import org.limewire.core.settings.UltrapeerSettings; import org.limewire.io.NetworkInstanceUtils; import org.limewire.net.SocketsManager; import org.limewire.net.TLSManager; import org.limewire.net.SocketsManager.ConnectType; import org.limewire.util.PrivilegedAccessor; import com.google.inject.AbstractModule; import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.Provider; import com.google.inject.Singleton; import com.google.inject.Stage; import com.google.inject.name.Named; import com.limegroup.gnutella.HostCatcher.EndpointObserver; import com.limegroup.gnutella.bootstrap.TcpBootstrap; import com.limegroup.gnutella.bootstrap.UDPHostCacheFactory; import com.limegroup.gnutella.connection.ConnectionCapabilities; import com.limegroup.gnutella.connection.ConnectionCapabilitiesDelegator; import com.limegroup.gnutella.connection.ConnectionLifecycleEvent; import com.limegroup.gnutella.connection.ConnectionLifecycleListener; import com.limegroup.gnutella.connection.GnutellaConnection; import com.limegroup.gnutella.connection.MessageReaderFactory; import com.limegroup.gnutella.connection.OutputRunner; import com.limegroup.gnutella.connection.RoutedConnection; import com.limegroup.gnutella.connection.RoutedConnectionFactory; import com.limegroup.gnutella.dht.DHTManager; import com.limegroup.gnutella.filters.IPFilter; import com.limegroup.gnutella.filters.SpamFilterFactory; import com.limegroup.gnutella.handshaking.HandshakeResponderFactory; import com.limegroup.gnutella.handshaking.HandshakeResponse; import com.limegroup.gnutella.handshaking.HeadersFactory; import com.limegroup.gnutella.messages.Message; import com.limegroup.gnutella.messages.MessageFactory; import com.limegroup.gnutella.messages.PingRequestFactory; import com.limegroup.gnutella.messages.QueryReplyFactory; import com.limegroup.gnutella.messages.QueryRequestFactory; import com.limegroup.gnutella.messages.vendor.CapabilitiesVMFactory; import com.limegroup.gnutella.messages.vendor.MessagesSupportedVendorMessage; import com.limegroup.gnutella.search.SearchResultHandler; import com.limegroup.gnutella.simpp.SimppManager; import com.limegroup.gnutella.util.LimeTestCase; import com.limegroup.gnutella.version.UpdateHandler; /** * PARTIAL unit tests for ConnectionManager. Makes sure HostCatcher is notified * of right events. */ @SuppressWarnings("all") public class ConnectionManagerTest extends LimeTestCase { private Injector injector; private TestHostCatcher CATCHER; private ConnectionListener LISTENER; private ConnectionManager connectionManager; private ConnectionServices connectionServices; private TestManagedConnectionFactory testConnectionFactory; private LifecycleManager lifecycleManager; private TLSManager TLSManager; public ConnectionManagerTest(String name) { super(name); } public static Test suite() { return buildTestSuite(ConnectionManagerTest.class); } public static void main(String[] args) { junit.textui.TestRunner.run(suite()); } public void setUp() throws Exception { setSettings(); injector = LimeTestUtils.createInjector(Stage.PRODUCTION, new AbstractModule() { @Override protected void configure() { bind(HostCatcher.class).to(TestHostCatcher.class); } }); finishSetUp(); TLSManager = injector.getInstance(NetworkManager.class); } private void finishSetUp() throws Exception { CATCHER = (TestHostCatcher)injector.getInstance(HostCatcher.class); LISTENER = new ConnectionListener(); connectionManager = injector.getInstance(ConnectionManager.class); connectionManager.addEventListener(LISTENER); testConnectionFactory = injector.getInstance(TestManagedConnectionFactory.class); lifecycleManager = injector.getInstance(LifecycleManager.class); connectionServices = injector.getInstance(ConnectionServices.class); launchAllBackends(); lifecycleManager.start(); // Currently, there are no default EVIL_HOSTS useragents. to test this, we need // to pick on someone, so it will be Morpheus =) String [] agents = {"morpheus"}; ConnectionSettings.EVIL_HOSTS.setValue( agents ); CATCHER.resetLatches(); CATCHER.endpoint = null; } private void setSettings() throws Exception { NetworkSettings.PORT.setValue(6346); ConnectionSettings.CONNECT_ON_STARTUP.setValue(false); ConnectionSettings.LOCAL_IS_PRIVATE.setValue(false); ConnectionSettings.EVER_ACCEPTED_INCOMING.setValue(true); UltrapeerSettings.FORCE_ULTRAPEER_MODE.setValue(true); UltrapeerSettings.EVER_ULTRAPEER_CAPABLE.setValue(true); ConnectionSettings.PREFERENCING_ACTIVE.setValue(false); UltrapeerSettings.NEED_MIN_CONNECT_TIME.setValue(true); ApplicationSettings.TOTAL_CONNECTION_TIME.setValue(0); ApplicationSettings.AVERAGE_CONNECTION_TIME.setValue(0); } public void tearDown() throws Exception { //Kill all connections if (connectionServices != null) { connectionServices.disconnect(); } if (lifecycleManager != null) { lifecycleManager.shutdown(); } Thread.sleep(500); } /** * Tests the method for allowing ultrapeer 2 ultrapeer connections. * * @throws Exception if an error occurs */ public void testAllowUltrapeer2UltrapeerConnection() throws Exception { HandshakeResponse hr = createTestResponse("Morpheus 3.3"); boolean allow = ((ConnectionManagerImpl)connectionManager).allowUltrapeer2UltrapeerConnection(hr); assertFalse("connection should not have been allowed", allow); hr = createTestResponse("Bearshare 3.3"); allow = ((ConnectionManagerImpl)connectionManager).allowUltrapeer2UltrapeerConnection(hr); assertTrue("connection should have been allowed", allow); hr = createTestResponse("LimeWire 3.3"); allow = ((ConnectionManagerImpl)connectionManager).allowUltrapeer2UltrapeerConnection(hr); assertFalse("connection should not have been allowed", allow); hr = createTestResponse("LimeWire 4.13.9"); allow = ((ConnectionManagerImpl)connectionManager).allowUltrapeer2UltrapeerConnection(hr); assertFalse("connection should not have been allowed", allow); hr = createTestResponse("LimeWire 4.14.8"); allow = ((ConnectionManagerImpl)connectionManager).allowUltrapeer2UltrapeerConnection(hr); assertFalse("connection should not have been allowed", allow); hr = createTestResponse("LimeWire 4.16.6"); allow = ((ConnectionManagerImpl)connectionManager).allowUltrapeer2UltrapeerConnection(hr); assertTrue("connection should have been allowed", allow); hr = createTestResponse("LimeWire 5.6.7"); allow = ((ConnectionManagerImpl)connectionManager).allowUltrapeer2UltrapeerConnection(hr); assertTrue("connection should not have been allowed", allow); hr = createTestResponse("Shareaza 3.3"); allow = ((ConnectionManagerImpl)connectionManager).allowUltrapeer2UltrapeerConnection(hr); assertTrue("connection should have been allowed", allow); } /** * Tests the method for allowing ultrapeer 2 leaf connections. * * @throws Exception if an error occurs */ public void testAllowUltrapeer2LeafConnection() throws Exception { HandshakeResponse hr = createTestResponse("Morpheus 3.3"); boolean allow = ConnectionManagerImpl.allowUltrapeer2LeafConnection(hr); assertFalse("connection should not have been allowed", allow); hr = createTestResponse("Bearshare 3.3"); allow = ConnectionManagerImpl.allowUltrapeer2LeafConnection(hr); assertTrue("connection should have been allowed", allow); hr = createTestResponse("LimeWire 3.3"); allow = ConnectionManagerImpl.allowUltrapeer2LeafConnection(hr); assertTrue("connection should have been allowed", allow); hr = createTestResponse("Shareaza 3.3"); allow = ConnectionManagerImpl.allowUltrapeer2LeafConnection(hr); assertTrue("connection should have been allowed", allow); } /** * Utility method for creating a set of headers with the specified user * agent value. * * @param userAgent the User-Agent to include in the headers * @return a new <tt>HandshakeResponse</tt> with the specified user agent * @throws IOException if an error occurs */ private static HandshakeResponse createTestResponse(String userAgent) throws IOException { Properties headers = new Properties(); headers.put("User-Agent", userAgent); return HandshakeResponse.createResponse(headers); } public void testSupernodeStatus() throws Exception { UltrapeerSettings.FORCE_ULTRAPEER_MODE.setValue(false); UltrapeerSettings.NEED_MIN_CONNECT_TIME.setValue(true); ConnectionManager mgr = connectionManager; // test preconditions assertTrue("should not start as supernode", !mgr.isSupernode()); assertTrue("should not be a shielded leaf", !mgr.isShieldedLeaf()); UltrapeerSettings.MIN_CONNECT_TIME.setValue(0); setConnectTime(); assertTrue("should start as supernode", mgr.isSupernode()); assertTrue("should not be leaf", !mgr.isShieldedLeaf()); // construct peers // u ==> i should be ultrapeer // l ==> i should be leaf TestManagedConnection u1, l1, l2; u1 = testConnectionFactory.createSupernodeClientConnection(true); l1 = testConnectionFactory.createClientSupernodeConnection(true); l2 = testConnectionFactory.createClientSupernodeConnection(true); // add a supernode => client connection initializeStart(u1); assertTrue("should still be supernode", mgr.isSupernode()); assertTrue("should still not be leaf", !mgr.isShieldedLeaf()); initializeDone(u1); assertTrue("should still be supernode", mgr.isSupernode()); assertTrue("should still not be leaf", !mgr.isShieldedLeaf()); mgr.remove(u1); assertTrue("should still be supernode", mgr.isSupernode()); assertTrue("should still not be leaf", !mgr.isShieldedLeaf()); // add a leaf -> supernode connection initializeStart(l1); assertTrue("should still be supernode", mgr.isSupernode()); assertTrue("should still not be leaf", !mgr.isShieldedLeaf()); initializeDone(l1); assertTrue("should not be supernode", !mgr.isSupernode()); assertTrue("should be leaf", mgr.isShieldedLeaf()); mgr.remove(l1); assertTrue("should be supernode", mgr.isSupernode()); assertTrue("should not be leaf", !mgr.isShieldedLeaf()); // test a strange condition // (two leaves start, second finishes then removes, // third removes then finishes) initializeStart(l1); initializeStart(l2); initializeDone(l2); assertTrue("should not be supernode", !mgr.isSupernode()); assertTrue("should be leaf", mgr.isShieldedLeaf()); mgr.remove(l2); assertTrue("should be supernode", mgr.isSupernode()); assertTrue("should not be leaf", !mgr.isShieldedLeaf()); mgr.remove(l1); assertTrue("should be supernode", mgr.isSupernode()); assertTrue("should not be leaf", !mgr.isShieldedLeaf()); initializeDone(l1); assertTrue("should be supernode", mgr.isSupernode()); assertTrue("should not be leaf", !mgr.isShieldedLeaf()); } /** * Tests the various connection-counting methods */ public void testGetNumberOfConnections() throws Exception { GnutellaConnection[] limeLeaves = new GnutellaConnection[30]; GnutellaConnection[] nonLimeLeaves = new GnutellaConnection[30]; GnutellaConnection[] limePeers = new GnutellaConnection[32]; GnutellaConnection[] nonLimePeers = new GnutellaConnection[32]; for(int i = 0; i < limeLeaves.length; i++) { limeLeaves[i] = testConnectionFactory.createSupernodeClientConnection(true); } for(int i = 0; i < nonLimeLeaves.length; i++) nonLimeLeaves[i] = testConnectionFactory.createSupernodeClientConnection(false); for(int i = 0; i < limePeers.length; i++) limePeers[i] = testConnectionFactory.createSupernodeSupernodeConnection(true); for(int i = 0; i < nonLimePeers.length; i++) nonLimePeers[i] = testConnectionFactory.createSupernodeSupernodeConnection(false); UltrapeerSettings.FORCE_ULTRAPEER_MODE.setValue(true); ConnectionManager mgr = connectionManager; setConnectTime(); // test preconditions assertTrue("should start as supernode", mgr.isSupernode()); assertTrue("should not be leaf", !mgr.isShieldedLeaf()); pretendConnected(); assertEquals(32, mgr.getNumFreeNonLeafSlots()); assertEquals(27, mgr.getNumFreeLimeWireNonLeafSlots()); assertEquals(30, mgr.getNumFreeLeafSlots()); assertEquals(28, mgr.getNumFreeLimeWireLeafSlots()); // Add 10 limewire leaves and 5 limewire peers. for(int i = 0; i < 10; i++) { initializeStart(limeLeaves[i]); initializeDone(limeLeaves[i]); } for(int i = 0; i < 5; i++) { initializeStart(limePeers[i]); initializeDone(limePeers[i]); } // Are now 10 lime leaves & 5 lime peers. // Equaling: 10 leaves & 5 peers. assertEquals(20, mgr.getNumFreeLeafSlots()); assertEquals(18, mgr.getNumFreeLimeWireLeafSlots()); assertEquals(27, mgr.getNumFreeNonLeafSlots()); assertEquals(24, mgr.getNumFreeLimeWireNonLeafSlots()); // Add 1 non lime leaf and 2 non limewire peers. for(int i = 0; i < 1; i++) { initializeStart(nonLimeLeaves[i]); initializeDone(nonLimeLeaves[i]); } for(int i = 0; i < 2; i++) { initializeStart(nonLimePeers[i]); initializeDone(nonLimePeers[i]); } // Are now 10 lime leaves, 1 non lime leaf, 5 lime peers, 2 non lime peers // Equaling: 11 leaves & 7 peers assertEquals(19, mgr.getNumFreeLeafSlots()); assertEquals(18, mgr.getNumFreeLimeWireLeafSlots()); assertEquals(25, mgr.getNumFreeNonLeafSlots()); assertEquals(24, mgr.getNumFreeLimeWireNonLeafSlots()); // Add a bunch of non lime peers & leaves. for(int i = 1; i < 15; i++) { initializeStart(nonLimeLeaves[i]); initializeDone(nonLimeLeaves[i]); } for(int i = 2; i < 15; i++) { initializeStart(nonLimePeers[i]); initializeDone(nonLimePeers[i]); } // There are now 15 non lime leaves & non lime peers connected, // 10 lime leaves, and 5 lime peers. // Equaling: 25 leaves and 20 peers. assertEquals(5, mgr.getNumFreeLeafSlots()); assertEquals(5, mgr.getNumFreeLimeWireLeafSlots()); assertEquals(12, mgr.getNumFreeNonLeafSlots()); assertEquals(12, mgr.getNumFreeLimeWireNonLeafSlots()); // Add 5 lime leaves and 12 lime peers for(int i = 10; i < 15; i++) { initializeStart(limeLeaves[i]); initializeDone(limeLeaves[i]); } for(int i = 5; i < 17; i++) { initializeStart(limePeers[i]); initializeDone(limePeers[i]); } // There are now 15 non lime leaves & non lime peers, // 15 lime leaves, and 17 lime peers. // Equaling: 30 leaves and 32 peers. assertEquals(0, mgr.getNumFreeLeafSlots()); assertEquals(0, mgr.getNumFreeLimeWireLeafSlots()); assertEquals(0, mgr.getNumFreeNonLeafSlots()); assertEquals(0, mgr.getNumFreeLimeWireNonLeafSlots()); // Now kill all but 2 of the non lime leaves // and all but 3 of non lime peers. for(int i = 14; i >= 2; i--) mgr.remove(nonLimeLeaves[i]); for(int i = 14; i >= 3; i--) mgr.remove(nonLimePeers[i]); // There are now 2 non lime leaves and 3 non lime peers, // and 15 lime leaves and 17 lime peers. // Equaling: 17 leaves and 20 peers assertEquals(13, mgr.getNumFreeLeafSlots()); assertEquals(13, mgr.getNumFreeLimeWireLeafSlots()); assertEquals(12, mgr.getNumFreeNonLeafSlots()); assertEquals(12, mgr.getNumFreeLimeWireNonLeafSlots()); // Now add 13 lime leaves and 12 lime peers. for(int i = 15; i < 28; i++) { initializeStart(limeLeaves[i]); initializeDone(limeLeaves[i]); } for(int i = 17; i < 29; i++) { initializeStart(limePeers[i]); initializeDone(limePeers[i]); } // There are now 2 non lime leaves and 3 non lime peers, // and 28 lime leaves and 29 lime peers // Equaling: 30 leaves and 32 peers assertEquals(0, mgr.getNumFreeLeafSlots()); assertEquals(0, mgr.getNumFreeLimeWireLeafSlots()); assertEquals(0, mgr.getNumFreeNonLeafSlots()); assertEquals(0, mgr.getNumFreeLimeWireNonLeafSlots()); // Now kill off the rest of the non lime peers and leaves. for(int i = 1; i >= 0; i--) mgr.remove(nonLimeLeaves[i]); for(int i = 2; i >= 0; i--) mgr.remove(nonLimePeers[i]); // There are now no non lime leaves and no non lime peers, // and 28 lime leaves and 29 lime peers // Equaling: 28 leaves and 29 peers assertEquals(2, mgr.getNumFreeLeafSlots()); assertEquals(0, mgr.getNumFreeLimeWireLeafSlots()); assertEquals(3, mgr.getNumFreeNonLeafSlots()); assertEquals(0, mgr.getNumFreeLimeWireNonLeafSlots()); } /** * Tests to make sure that a connection does not succeed with an * unreachable host. */ public void testUnreachableHost() throws Exception { CATCHER.endpoint = new ExtendedEndpoint("1.2.3.4", 5000); connectionServices.connect(); assertTrue(CATCHER.waitForFailure(10000)); assertEquals(0, CATCHER.getSuccessCount()); } /** * Test to make sure tests does not succeed with a host reporting * the wrong protocol. */ public void testWrongProtocolHost() throws Exception { CATCHER.endpoint = new ExtendedEndpoint("www.yahoo.com", 80); connectionServices.connect(); assertTrue(CATCHER.waitForFailure(10000)); assertEquals(0, CATCHER.getSuccessCount()); } /** * Test to make sure that a good host is successfully connected to. */ public void testGoodHost() throws Exception { CATCHER.endpoint = new ExtendedEndpoint("localhost", Backend.BACKEND_PORT); connectionServices.connect(); assertTrue(CATCHER.waitForSuccess(5000)); assertEquals(0, CATCHER.getFailureCount()); Thread.sleep(1000); // let capVM send assertEquals(1, connectionManager.getInitializedConnections().size()); RoutedConnection mc = connectionManager.getInitializedConnections().get(0); assertTrue(mc.isTLSCapable()); assertFalse(mc.isTLSEncoded()); } public void testGoodTLSHost() throws Exception { TLSManager.setOutgoingTLSEnabled(true); CATCHER.endpoint = new ExtendedEndpoint("localhost", Backend.BACKEND_PORT); CATCHER.endpoint.setTLSCapable(true); connectionServices.connect(); assertTrue(CATCHER.waitForSuccess(5000)); assertEquals(0, CATCHER.getFailureCount()); Thread.sleep(1000); // let capVM send assertEquals(1, connectionManager.getInitializedConnections().size()); RoutedConnection mc = connectionManager.getInitializedConnections().get(0); assertTrue(mc.isTLSCapable()); assertTrue(mc.isTLSEncoded()); } public void testGoodTLSHostNotUsedIfNoSetting() throws Exception { TLSManager.setOutgoingTLSEnabled(false); CATCHER.endpoint = new ExtendedEndpoint("localhost", Backend.BACKEND_PORT); CATCHER.endpoint.setTLSCapable(true); connectionServices.connect(); assertTrue(CATCHER.waitForSuccess(5000)); assertEquals(0, CATCHER.getFailureCount()); Thread.sleep(1000); // let capVM send assertEquals(1, connectionManager.getInitializedConnections().size()); RoutedConnection mc = connectionManager.getInitializedConnections().get(0); assertTrue(mc.isTLSCapable()); assertFalse(mc.isTLSEncoded()); } /** * Tests to make sure that a host is still added to the host * catcher as a connection that was made (at least temporarily) even * if the server sent a 503. */ public void testRejectHost() throws Exception { CATCHER.endpoint = new ExtendedEndpoint("localhost", Backend.REJECT_PORT); connectionServices.connect(); assertTrue(CATCHER.waitForSuccess(5000)); assertEquals(0, CATCHER.getFailureCount()); } public void testRecordConnectionTime() throws Exception{ ApplicationSettings.AVERAGE_CONNECTION_TIME.setValue(0); ApplicationSettings.TOTAL_CONNECTION_TIME.setValue(0); ApplicationSettings.TOTAL_CONNECTIONS.setValue(0); ConnectionManager mgr = connectionManager; assertFalse(mgr.isConnected()); CATCHER.endpoint = new ExtendedEndpoint("localhost", Backend.BACKEND_PORT); //try simple connect-disconnect mgr.connect(); LISTENER.getLock().lock(); try { mgr.connect(); LISTENER.waitForInitialized(2000); } finally { LISTENER.getLock().unlock(); } mgr.disconnect(false); long totalConnect = ApplicationSettings.TOTAL_CONNECTION_TIME.getValue(); long averageTime = ApplicationSettings.AVERAGE_CONNECTION_TIME.getValue(); assertEquals(totalConnect, averageTime); mgr.connect(); Thread.sleep(6000); mgr.disconnect(false); assertGreaterThan(totalConnect+5800, ApplicationSettings.TOTAL_CONNECTION_TIME.getValue()); assertLessThan(totalConnect+6500, ApplicationSettings.TOTAL_CONNECTION_TIME.getValue()); assertGreaterThan(averageTime, ApplicationSettings.AVERAGE_CONNECTION_TIME.getValue()); assertGreaterThan((totalConnect+5800)/2, ApplicationSettings.AVERAGE_CONNECTION_TIME.getValue()); assertLessThan((totalConnect+6500)/2, ApplicationSettings.AVERAGE_CONNECTION_TIME.getValue()); //try disconnecting twice in a row mgr.disconnect(false); assertGreaterThan(totalConnect+5800, ApplicationSettings.TOTAL_CONNECTION_TIME.getValue()); assertLessThan(totalConnect+6500, ApplicationSettings.TOTAL_CONNECTION_TIME.getValue()); assertGreaterThan(averageTime, ApplicationSettings.AVERAGE_CONNECTION_TIME.getValue()); //test time changed during session long now = System.currentTimeMillis(); PrivilegedAccessor.setValue(connectionManager, "_connectTime", now + (60L * 60L * 1000L)); mgr.disconnect(false); assertGreaterThan(totalConnect+5800, ApplicationSettings.TOTAL_CONNECTION_TIME.getValue()); assertLessThan(totalConnect+6500, ApplicationSettings.TOTAL_CONNECTION_TIME.getValue()); assertGreaterThan((totalConnect+5800)/2, ApplicationSettings.AVERAGE_CONNECTION_TIME.getValue()); assertLessThan((totalConnect+6500)/2, ApplicationSettings.AVERAGE_CONNECTION_TIME.getValue()); } public void testGetCurrentAverageUptime() throws Exception{ ApplicationSettings.AVERAGE_CONNECTION_TIME.setValue(30L*60L*1000L); ApplicationSettings.TOTAL_CONNECTION_TIME.setValue(60L*60L*1000L); ApplicationSettings.TOTAL_CONNECTIONS.setValue(2); ConnectionManager mgr = connectionManager; assertFalse(mgr.isConnected()); CATCHER.endpoint = new ExtendedEndpoint("localhost", Backend.BACKEND_PORT); //try simple connect-disconnect LISTENER.getLock().lock(); try { mgr.connect(); LISTENER.waitForInitialized(2000); } finally { LISTENER.getLock().unlock(); } if(!mgr.isConnected()) fail("couldn't connect!"); Thread.sleep(2000); //this should have lowered average time assertLessThan((30L*60L*1000L), mgr.getCurrentAverageUptime()); assertGreaterThan((((60L*60L*1000L)+2L*1000L)/3L)-1, mgr.getCurrentAverageUptime()); assertEquals(2,ApplicationSettings.TOTAL_CONNECTIONS.getValue()); mgr.disconnect(false); long avgtime = ApplicationSettings.AVERAGE_CONNECTION_TIME.getValue(); assertLessThan((30L*60L*1000L), avgtime); assertGreaterThan((60L*60L*1000L)+(2L*1000L), ApplicationSettings.TOTAL_CONNECTION_TIME.getValue()); assertEquals(3, ApplicationSettings.TOTAL_CONNECTIONS.getValue()); //this should not change anything: long x = mgr.getCurrentAverageUptime(); assertEquals(x, mgr.getCurrentAverageUptime()); assertEquals(avgtime, ApplicationSettings.AVERAGE_CONNECTION_TIME.getValue()); } public void testClassCFiltering() throws Exception { setSettings(); injector = LimeTestUtils.createInjector(Stage.PRODUCTION, new AbstractModule() { @Override protected void configure() { bind(HostCatcher.class).to(TestHostCatcher2.class); } }); finishSetUp(); ServerSocket s = new ServerSocket(10000); try { ConnectionSettings.FILTER_CLASS_C.setValue(true); TestHostCatcher2 catcher = (TestHostCatcher2) this.CATCHER; TestConnectionObserver observer = new TestConnectionObserver(); ConnectionManager cm = connectionManager; cm.addEventListener(observer); cm.connect(); EndpointObserver eo = catcher.observers.poll(1000, TimeUnit.MILLISECONDS); // a connection should be initialized. eo.handleEndpoint(new Endpoint("127.0.0.1", 10000)); ConnectionLifecycleEvent event; do { event = observer.evts.poll(1000, TimeUnit.MILLISECONDS); } while( !event.isConnectionInitializingEvent()); // we should be still looking for more connections eo = catcher.observers.poll(1000, TimeUnit.MILLISECONDS); // adding a new endpoint from the same class C network should not trigger a connection Endpoint e2 = new Endpoint("127.0.0.2", 10000); eo.handleEndpoint(e2); assertNull(observer.evts.poll(1000, TimeUnit.MILLISECONDS)); // we should be still looking for more connections eo = catcher.observers.poll(1000, TimeUnit.MILLISECONDS); // this is a different class C - it should trigger an event. eo.handleEndpoint(new Endpoint("127.0.1.1", 20000)); do { event = observer.evts.poll(1000, TimeUnit.MILLISECONDS); } while( !event.isConnectionInitializingEvent()); // after the connection gets closed we should get the endpoint back to // hostcatcher s.accept().close(); assertSame(e2,catcher.endpoints.poll(1000,TimeUnit.MILLISECONDS)); // two closed events - one for .1 and one for 1.1 do { event = observer.evts.poll(2000, TimeUnit.MILLISECONDS); } while( !event.isConnectionClosedEvent()); do { event = observer.evts.poll(2000, TimeUnit.MILLISECONDS); } while( !event.isConnectionClosedEvent()); // now we should be able to establish a second connection eo.handleEndpoint(new Endpoint("127.0.0.2", 10000)); do { event = observer.evts.poll(1000, TimeUnit.MILLISECONDS); } while( !event.isConnectionInitializingEvent()); // if we allow more than one connection per class C, we'll be able to // add a second connection to the same class ConnectionSettings.FILTER_CLASS_C.setValue(false); eo.handleEndpoint(new Endpoint("127.0.0.1", 10000)); do { event = observer.evts.poll(1000, TimeUnit.MILLISECONDS); } while( !event.isConnectionInitializingEvent()); assertEquals(2,cm.getNumConnections()); cm.removeEventListener(observer); } finally { s.close(); } } private void setConnectTime() throws Exception { PrivilegedAccessor.setValue(connectionManager, "_connectTime", 0); } private void initializeStart(RoutedConnection c) throws Exception { // Need to setup the _outputRunner member of c as well... PrivilegedAccessor.setValue(c, "_outputRunner", new NullOutputRunner() ); PrivilegedAccessor.invokeMethod( connectionManager, "connectionInitializingIncoming", new Object[] { c }, new Class[] { RoutedConnection.class} ); } private void initializeDone(GnutellaConnection c) throws Exception { PrivilegedAccessor.invokeMethod( connectionManager, "connectionInitialized", new Object[] { c }, new Class[] { RoutedConnection.class} ); } /** * Test host catcher that allows us to return endpoints that we * specify when our test framework requests endpoints to connect * to. */ @Singleton private static class TestHostCatcher extends HostCatcher { private volatile ExtendedEndpoint endpoint; private volatile CountDownLatch successLatch; private volatile CountDownLatch failureLatch; private volatile AtomicInteger successes; private volatile AtomicInteger failures; @Inject TestHostCatcher( @Named("backgroundExecutor") ScheduledExecutorService backgroundExecutor, ConnectionServices connectionServices, Provider<ConnectionManager> connectionManager, Provider<UDPService> udpService, Provider<DHTManager> dhtManager, Provider<QueryUnicaster> queryUnicaster, Provider<IPFilter> ipFilter, Provider<MulticastService> multicastService, UniqueHostPinger uniqueHostPinger, UDPHostCacheFactory udpHostCacheFactory, PingRequestFactory pingRequestFactory, NetworkInstanceUtils networkInstanceUtils, TcpBootstrap tcpBootstrap) { super(backgroundExecutor, connectionServices, connectionManager, udpService, dhtManager, queryUnicaster, ipFilter, multicastService, uniqueHostPinger, udpHostCacheFactory, pingRequestFactory, networkInstanceUtils, tcpBootstrap); resetLatches(); } void resetLatches() { successLatch = new CountDownLatch(1); failureLatch = new CountDownLatch(1); successes = new AtomicInteger(); failures = new AtomicInteger(); } @Override protected ExtendedEndpoint getAnEndpointInternal() { if(endpoint == null) return null; else { ExtendedEndpoint ret = endpoint; endpoint = null; return ret; } } @Override public void doneWithConnect(Endpoint e, boolean success) { if (success) { successLatch.countDown(); successes.incrementAndGet(); } else { failureLatch.countDown(); failures.incrementAndGet(); } } boolean waitForSuccess(int millis) throws Exception { return successLatch.await(millis, TimeUnit.MILLISECONDS); } boolean waitForFailure(int millis) throws Exception { return failureLatch.await(millis, TimeUnit.MILLISECONDS); } long getSuccessCount() { return successes.get(); } long getFailureCount() { return failures.get(); } // Overridden because initialize() was previously overridden, but that missed // setting up some required members. Now, the code which was intended to be // skipped was moved into this function (on the base class) @Override public void scheduleServices() { } } @Singleton private static class TestHostCatcher2 extends TestHostCatcher { @Inject TestHostCatcher2( @Named("backgroundExecutor") ScheduledExecutorService backgroundExecutor, ConnectionServices connectionServices, Provider<ConnectionManager> connectionManager, Provider<UDPService> udpService, Provider<DHTManager> dhtManager, Provider<QueryUnicaster> queryUnicaster, @Named("hostileFilter") Provider<IPFilter> ipFilter, Provider<MulticastService> multicastService, UniqueHostPinger uniqueHostPinger, UDPHostCacheFactory udpHostCacheFactory, PingRequestFactory pingRequestFactory, NetworkInstanceUtils networkInstanceUtils, TcpBootstrap tcpBootstrap) { super(backgroundExecutor, connectionServices, connectionManager, udpService, dhtManager, queryUnicaster, ipFilter, multicastService, uniqueHostPinger, udpHostCacheFactory, pingRequestFactory, networkInstanceUtils, tcpBootstrap); } final BlockingQueue<EndpointObserver> observers = new ArrayBlockingQueue<EndpointObserver>(100); final BlockingQueue<Endpoint> endpoints = new ArrayBlockingQueue<Endpoint>(100); @Override public void getAnEndpoint(EndpointObserver observer) { assertTrue(observers.offer(observer)); } @Override public boolean add(Endpoint e, boolean asdf) { assertTrue(endpoints.offer(e)); return false; } } private static class TestConnectionObserver implements ConnectionLifecycleListener { final BlockingQueue<ConnectionLifecycleEvent> evts = new ArrayBlockingQueue<ConnectionLifecycleEvent>(100); public void handleConnectionLifecycleEvent(ConnectionLifecycleEvent evt) { assertTrue(evts.offer(evt)); } } @Singleton private static class TestManagedConnectionFactory implements RoutedConnectionFactory { private final Provider<ConnectionManager> connectionManager; private final NetworkManager networkManager; private final QueryRequestFactory queryRequestFactory; private final HeadersFactory headersFactory; private final HandshakeResponderFactory handshakeResponderFactory; private final QueryReplyFactory queryReplyFactory; private final Provider<MessageDispatcher> messageDispatcher; private final Provider<NetworkUpdateSanityChecker> networkUpdateSanityChecker; private final Provider<SearchResultHandler> searchResultHandler; private final CapabilitiesVMFactory capabilitiesVMFactory; private final SocketsManager socketsManager; private final Provider<Acceptor> acceptor; private final MessagesSupportedVendorMessage supportedVendorMessage; private final Provider<SimppManager> simppManager; private final Provider<UpdateHandler> updateHandler; private final Provider<ConnectionServices> connectionServices; private final GuidMapManager guidMapManager; private final SpamFilterFactory spamFilterFactory; private final MessageReaderFactory messageReaderFactory; private final MessageFactory messageFactory; private final ApplicationServices applicationServices; private final NetworkInstanceUtils networkInstanceUtils; @Inject public TestManagedConnectionFactory(Provider<ConnectionManager> connectionManager, NetworkManager networkManager, QueryRequestFactory queryRequestFactory, HeadersFactory headersFactory, HandshakeResponderFactory handshakeResponderFactory, QueryReplyFactory queryReplyFactory, Provider<MessageDispatcher> messageDispatcher, Provider<NetworkUpdateSanityChecker> networkUpdateSanityChecker, Provider<SearchResultHandler> searchResultHandler, CapabilitiesVMFactory capabilitiesVMFactory, SocketsManager socketsManager, Provider<Acceptor> acceptor, MessagesSupportedVendorMessage supportedVendorMessage, Provider<SimppManager> simppManager, Provider<UpdateHandler> updateHandler, Provider<ConnectionServices> connectionServices, GuidMapManager guidMapManager, SpamFilterFactory spamFilterFactory, MessageReaderFactory messageReaderFactory, MessageFactory messageFactory, ApplicationServices applicationServices, NetworkInstanceUtils networkInstanceUtils) { this.networkManager = networkManager; this.queryRequestFactory = queryRequestFactory; this.headersFactory = headersFactory; this.handshakeResponderFactory = handshakeResponderFactory; this.connectionManager = connectionManager; this.queryReplyFactory = queryReplyFactory; this.messageDispatcher = messageDispatcher; this.networkUpdateSanityChecker = networkUpdateSanityChecker; this.searchResultHandler = searchResultHandler; this.capabilitiesVMFactory = capabilitiesVMFactory; this.socketsManager = socketsManager; this.acceptor = acceptor; this.supportedVendorMessage = supportedVendorMessage; this.simppManager = simppManager; this.updateHandler = updateHandler; this.connectionServices = connectionServices; this.guidMapManager = guidMapManager; this.spamFilterFactory = spamFilterFactory; this.messageReaderFactory = messageReaderFactory; this.messageFactory = messageFactory; this.applicationServices = applicationServices; this.networkInstanceUtils = networkInstanceUtils; } public RoutedConnection createRoutedConnection(String host, int port) { return createRoutedConnection(host, port, ConnectType.PLAIN); } public GnutellaConnection createRoutedConnection(String host, int port, ConnectType type) { return new TestManagedConnection(connectionManager.get(), networkManager, queryRequestFactory, headersFactory, handshakeResponderFactory, queryReplyFactory, messageDispatcher.get(), networkUpdateSanityChecker.get(), searchResultHandler.get() , capabilitiesVMFactory, socketsManager, acceptor.get(), supportedVendorMessage, simppManager, updateHandler, connectionServices, guidMapManager, spamFilterFactory, messageReaderFactory, messageFactory, applicationServices, networkInstanceUtils); } public TestManagedConnection createTestConnection() { return new TestManagedConnection(connectionManager.get(), networkManager, queryRequestFactory, headersFactory, handshakeResponderFactory, queryReplyFactory, messageDispatcher.get(), networkUpdateSanityChecker.get(), searchResultHandler.get() , capabilitiesVMFactory, socketsManager, acceptor.get(), supportedVendorMessage, simppManager, updateHandler, connectionServices, guidMapManager, spamFilterFactory, messageReaderFactory, messageFactory, applicationServices, networkInstanceUtils); } public TestManagedConnection createSupernodeClientConnection(boolean lime) { TestManagedConnection connection = createTestConnection(); connection.setIsLimeWire(lime); connection.configureSupernodeClient(); return connection; } public TestManagedConnection createClientSupernodeConnection(boolean lime) { TestManagedConnection connection = createTestConnection(); connection.setIsLimeWire(lime); connection.configureClientSuperNode(); return connection; } public TestManagedConnection createSupernodeSupernodeConnection(boolean lime) { TestManagedConnection connection = createTestConnection(); connection.setIsLimeWire(lime); connection.configureSuperNodeSuperNode(); return connection; } public GnutellaConnection createRoutedConnection(Socket socket) { return null; } } private static class TestManagedConnection extends GnutellaConnection { private boolean isOutgoing; private int sent; private int received; private static int lastHost = 0; private boolean isLime; private boolean isClientSupernodeConnection; private boolean isSupernodeClientConnection; private boolean isSupernodeSupernodeConnection; public TestManagedConnection(ConnectionManager connectionManager, NetworkManager networkManager, QueryRequestFactory queryRequestFactory, HeadersFactory headersFactory, HandshakeResponderFactory handshakeResponderFactory, QueryReplyFactory queryReplyFactory, MessageDispatcher messageDispatcher, NetworkUpdateSanityChecker networkUpdateSanityChecker, SearchResultHandler searchResultHandler, CapabilitiesVMFactory capabilitiesVMFactory, SocketsManager socketsManager, Acceptor acceptor, MessagesSupportedVendorMessage supportedVendorMessage, Provider<SimppManager> simppManager, Provider<UpdateHandler> updateHandler, Provider<ConnectionServices> connectionServices, GuidMapManager guidMapManager, SpamFilterFactory spamFilterFactory, MessageReaderFactory messageReaderFactory, MessageFactory messageFactory, ApplicationServices applicationServices, NetworkInstanceUtils networkInstanceUtils) { super("1.2.3." + ++lastHost, 6346, ConnectType.PLAIN, connectionManager, networkManager, queryRequestFactory, headersFactory, handshakeResponderFactory, queryReplyFactory, messageDispatcher, networkUpdateSanityChecker, searchResultHandler, capabilitiesVMFactory, socketsManager, acceptor, supportedVendorMessage, simppManager, updateHandler, connectionServices, guidMapManager, spamFilterFactory, messageReaderFactory, messageFactory, applicationServices, null, null, networkInstanceUtils); } @Override public boolean isOutgoing() { return isOutgoing; } public void setIsOutgoing(boolean isOutgoing) { this.isOutgoing = isOutgoing; } @Override public int getNumMessagesSent() { return sent; } public void setNumMessagesSent(int sent) { this.sent = sent; } @Override public int getNumMessagesReceived() { return received; } public void setNumMessagesReceived(int received) { this.received = received; } public void configureDefaults() { setIsOutgoing(false); setNumMessagesSent(0); setNumMessagesReceived(0); } public void configureSupernodeClient() { setIsSupernodeClientConnection(true); setIsClientSupernodeConnection(false); } public void configureClientSuperNode() { setIsSupernodeClientConnection(false); setIsClientSupernodeConnection(true); } public void configureSuperNodeSuperNode() { setIsSupernodeSupernodeConnection(true); setIsClientSupernodeConnection(false); setIsSupernodeClientConnection(false); } public void setIsSupernodeSupernodeConnection(boolean isSupernodeSupernodeConnection) { this.isSupernodeSupernodeConnection = isSupernodeSupernodeConnection; } public void setIsClientSupernodeConnection(boolean isClientSupernodeConnection) { this.isClientSupernodeConnection = isClientSupernodeConnection; } public void setIsSupernodeClientConnection(boolean isSupernodeClientConnection) { this.isSupernodeClientConnection = isSupernodeClientConnection; } public void setIsLimeWire(boolean isLime) { this.isLime = isLime; } private final AtomicReference<ConnectionCapabilities> connectionCapabilitiesRef = new AtomicReference<ConnectionCapabilities>(); // return a stubbed capabilities that is backed by the basic ones. @Override public ConnectionCapabilities getConnectionCapabilities() { if (connectionCapabilitiesRef.get() == null) connectionCapabilitiesRef.compareAndSet(null, new StubCapabilities(super .getConnectionCapabilities())); return connectionCapabilitiesRef.get(); } private class StubCapabilities extends ConnectionCapabilitiesDelegator { public StubCapabilities(ConnectionCapabilities delegate) { super(delegate); } @Override public boolean isSupernodeSupernodeConnection() { return isSupernodeSupernodeConnection; } @Override public boolean isClientSupernodeConnection() { return isClientSupernodeConnection; } @Override public boolean isSupernodeClientConnection() { return isSupernodeClientConnection; } @Override public boolean isLimeWire() { return isLime; } } } private void pretendConnected() throws Exception { PrivilegedAccessor.setValue(connectionManager, "_disconnectTime", 0); PrivilegedAccessor.invokeMethod(connectionManager, "setPreferredConnections"); } private class NullOutputRunner implements OutputRunner { // Overridden methods do nothing public void send(Message m) {} public void shutdown() {} public Object inspect() { return "NullRunner"; } } private static class ConnectionListener implements ConnectionLifecycleListener { private final Lock lock = new ReentrantLock(); private final Condition connected = lock.newCondition(); private final Condition connecting = lock.newCondition(); private final Condition capabilities = lock.newCondition(); private final Condition closed = lock.newCondition(); private final Condition initialized = lock.newCondition(); private final Condition initializing = lock.newCondition(); private final Condition disconnected = lock.newCondition(); private final Condition noInternet = lock.newCondition(); public void handleConnectionLifecycleEvent(ConnectionLifecycleEvent evt) { try { lock.lock(); switch(evt.getType()) { case CONNECTED: connected.signal(); break; case CONNECTING: connecting.signal(); break; case CONNECTION_CAPABILITIES: capabilities.signal(); break; case CONNECTION_CLOSED: closed.signal(); break; case CONNECTION_INITIALIZED: initialized.signal(); break; case CONNECTION_INITIALIZING: initializing.signal(); break; case DISCONNECTED: disconnected.signal(); break; case NO_INTERNET: noInternet.signal(); break; default: throw new IllegalStateException("invalid type: " + evt.getType()); } } finally { lock.unlock(); } } boolean waitForConnected(int millis) throws Exception { return await(connected, millis); } boolean waitForConnecting(int millis) throws Exception { return await(connecting, millis); } boolean waitForCapabilities(int millis) throws Exception { return await(capabilities, millis); } boolean waitForClosed(int millis) throws Exception { return await(closed, millis); } boolean waitForInitialized(int millis) throws Exception { return await(initialized, millis); } boolean waitForInitializing(int millis) throws Exception { return await(initializing, millis); } boolean waitForDisconnected(int millis) throws Exception { return await(disconnected, millis); } boolean waitForNoInternet(int millis) throws Exception { return await(noInternet, millis); } private boolean await(Condition x, int millis) throws Exception { return x.await(millis, TimeUnit.MILLISECONDS); } Lock getLock() { return lock; } } }