package com.limegroup.gnutella;
import java.net.Socket;
import java.util.List;
import java.util.Set;
import org.limewire.io.Connectable;
import org.limewire.io.GUID;
import org.limewire.io.IpPort;
import org.limewire.listener.EventListener;
import org.limewire.net.ConnectionAcceptor;
import org.limewire.net.SocketsManager.ConnectType;
import com.limegroup.gnutella.connection.ConnectionLifecycleEvent;
import com.limegroup.gnutella.connection.ConnectionLifecycleListener;
import com.limegroup.gnutella.connection.GnutellaConnectionEvent;
import com.limegroup.gnutella.connection.RoutedConnection;
import com.limegroup.gnutella.handshaking.HandshakeResponse;
import com.limegroup.gnutella.handshaking.HandshakeStatus;
import com.limegroup.gnutella.messages.Message.Network;
import com.limegroup.gnutella.messages.vendor.QueryStatusResponse;
import com.limegroup.gnutella.util.EventDispatcher;
/**
* The list of all RoutedConnection's. Provides a factory method for creating
* user-requested outgoing connections, accepts incoming connections, and
* fetches "automatic" outgoing connections as needed. Creates threads for
* handling these connections when appropriate.
*
* Because this is the only list of all connections, it plays an important role
* in message broadcasting. For this reason, the code is highly tuned to avoid
* locking in the getInitializedConnections() methods. Adding and removing
* connections is a slower operation.<p>
*
* LimeWire follows the following connection strategy:<br>
* As a leaf, LimeWire will ONLY connect to 'good' Ultrapeers. The definition
* of good is constantly changing. For a current view of 'good', review
* HandshakeResponse.isGoodUltrapeer(). LimeWire leaves will NOT deny
* a connection to an ultrapeer even if they've reached their maximum
* desired number of connections (currently 3). This means that if 4
* connections resolve simultaneously, the leaf will remain connected to all 4.
* <br>
* As an Ultrapeer, LimeWire will seek outgoing connections for 5 less than
* the number of it's desired peer slots. This is done so that newcomers
* on the network have a better chance of finding an ultrapeer with a slot
* open. LimeWire ultrapeers will allow ANY other ultrapeer to connect to it,
* and to ensure that the network does not become too LimeWire-centric, it
* reserves 3 slots for non-LimeWire peers. LimeWire ultrapeers will allow
* ANY leaf to connect, so long as there are atleast 15 slots open. Beyond
* that number, LimeWire will only allow 'good' leaves. To see what consitutes
* a good leaf, view HandshakeResponse.isGoodLeaf(). To ensure that the
* network does not remain too LimeWire-centric, it reserves 2 slots for
* non-LimeWire leaves.<p>
*
* ConnectionManager has methods to get up and downstream bandwidth, but it
* doesn't quite fit the BandwidthTracker interface.
*/
public interface ConnectionManager extends ConnectionAcceptor,
EventDispatcher<ConnectionLifecycleEvent, ConnectionLifecycleListener>, EventListener<GnutellaConnectionEvent> {
/** How many connect back requests to send if we have a single connection */
public static final int CONNECT_BACK_REDUNDANT_REQUESTS = 3;
/**
* The number of leaf connections reserved for non LimeWire clients.
* This is done to ensure that the network is not solely LimeWire centric.
*/
public static final int RESERVED_NON_LIMEWIRE_LEAVES = 2;
/**
* Links the ConnectionManager up with the other back end pieces and
* launches the ConnectionWatchdog and the initial ConnectionFetchers.
*/
public void start();
/**
* Create a new connection, allowing it to initialize and loop for messages on a new thread.
*/
public void createConnectionAsynchronously(String hostname, int portnum, ConnectType type);
/**
* Create an incoming connection.
*
* If the connection can support asynchronous messaging, this method will return
* immediately. Otherwise, this will block forever while the connection handshakes
* and then loops for messages (it will return when the connection dies).
*/
void acceptConnection(Socket socket);
/**
* Removes the specified connection from currently active connections, also
* removing this connection from routing tables and modifying active
* connection fetchers accordingly.
*
* @param mc the <tt>RoutedConnection</tt> instance to remove
*/
public void remove(RoutedConnection mc);
/**
* True if this is currently or wants to be a supernode,
* otherwise false.
*/
public boolean isSupernode();
/** Return true if we are not a private address, have been ultrapeer capable
* in the past, and are not being shielded by anybody, we don't have UP
* mode disabled AND we are not exclusively a DHT node.
*/
public boolean isSupernodeCapable();
/**
* @return if we are currently using a http or socks4/5 proxy to connect.
*/
public boolean isBehindProxy();
/**
* Tells whether or not we're actively being a supernode to anyone.
*/
public boolean isActiveSupernode();
/**
* Returns true if this is a leaf node with a connection to a ultrapeer. It
* is not required that the ultrapeer support query routing, though that is
* generally the case.
*/
public boolean isShieldedLeaf();
/**
* Returns true if this is a super node with a connection to a leaf.
*/
public boolean hasSupernodeClientConnection();
/**
* Returns whether or not this node has any available connection
* slots. This is only relevant for Ultrapeers -- leaves will
* always return <tt>false</tt> to this call since they do not
* accept any incoming connections, at least for now.
*
* @return <tt>true</tt> if this node is an Ultrapeer with free
* leaf or Ultrapeer connections slots, otherwise <tt>false</tt>
*/
public boolean hasFreeSlots();
/**
* Returns whether this (probably) has a connection to the given host. This
* method is currently implemented by iterating through all connections and
* comparing addresses but not ports. (Incoming connections use ephemeral
* ports.) As a result, this test may conservatively return true even if
* this is not connected to <tt>host</tt>. Likewise, it may it mistakenly
* return false if <tt>host</tt> is a multihomed system. In the future,
* additional connection headers may make the test more precise.
*
* @return true if this is probably connected to <tt>host</tt>
*/
boolean isConnectedTo(String hostName);
/**
* Returns true if we're currently attempting to connect to a particular host.
* This checks both the Ip & Port.
*/
boolean isConnectingTo(IpPort host);
/**
* @return the number of connections, which is greater than or equal
* to the number of initialized connections.
*/
public int getNumConnections();
/**
* @return the number of initialized connections, which is less than or
* equals to the number of connections.
*/
public int getNumInitializedConnections();
/**
* @return the number of initializedclient connections, which is less than
* or equals to the number of connections.
*/
public int getNumInitializedClientConnections();
/**
*@return the number of initialized connections for which
* isClientSupernodeConnection is true.
*/
public int getNumClientSupernodeConnections();
/**
*@return the number of ultrapeer -> ultrapeer connections.
*/
public int getNumUltrapeerConnections();
/**
*@return the number of old unrouted connections.
*/
public int getNumOldConnections();
/**
* @return the number of free leaf slots.
*/
public int getNumFreeLeafSlots();
/**
* @return the number of free leaf slots that LimeWires can connect to.
*/
public int getNumFreeLimeWireLeafSlots();
/**
* @return the number of free non-leaf slots.
*/
public int getNumFreeNonLeafSlots();
/**
* @return the number of free non-leaf slots that LimeWires can connect to.
*/
public int getNumFreeLimeWireNonLeafSlots();
/**
* Returns true if we've made a locale-matching connection (or don't
* want any at all).
*/
public boolean isLocaleMatched();
/**
* @return the number of locale reserved slots to be filled
*
* An ultrapeer may not have Free LimeWire Non Leaf Slots but may still
* have free slots that are reserved for locales
*/
public int getNumLimeWireLocalePrefSlots();
/**
* Determines if we've reached our maximum number of preferred connections.
*/
public boolean isFullyConnected();
/**
* Returns whether or not the client has an established connection with
* another Gnutella client.
*
* @return <tt>true</tt> if the client is currently connected to
* another Gnutella client, <tt>false</tt> otherwise
*/
public boolean isConnected();
/**
* Returns whether or not we are currently attempting to connect to the
* network.
*/
public boolean isConnecting();
/**
* Takes a snapshot of the upstream and downstream bandwidth since the last
* call to measureBandwidth.
* @see BandwidthTracker#measureBandwidth
*/
public void measureBandwidth();
/**
* Returns the upstream bandwidth in kbytes/sec between the last two calls
* to measureBandwidth.
* @see BandwidthTracker#measureBandwidth
*/
public float getMeasuredUpstreamBandwidth();
/**
* Returns the downstream bandwidth in kbytes/sec between the last two calls
* to measureBandwidth.
* @see BandwidthTracker#measureBandwidth
*/
public float getMeasuredDownstreamBandwidth();
/**
* Checks if the connection received can be accepted,
* based upon the type of connection (e.g. client, ultrapeer,
* temporary etc).
* @param c The connection we received, for which to
* test if we have incoming slot.
* @return true, if we have incoming slot for the connection received,
* false otherwise
*/
public HandshakeStatus allowConnectionAsLeaf(HandshakeResponse hr);
/**
* Checks if the connection received can be accepted,
* based upon the type of connection (e.g. client, ultrapeer,
* temporary etc).
* @param c The connection we received, for which to
* test if we have incoming slot.
* @return true, if we have incoming slot for the connection received,
* false otherwise
*/
public HandshakeStatus allowConnection(HandshakeResponse hr);
/**
* Checks if there is any available slot of any kind.
* @return true, if we have incoming slot of some kind,
* false otherwise
*/
public boolean allowAnyConnection();
/**
* Returns true if this has slots for an incoming connection, <b>without
* accounting for this' ultrapeer capabilities</b>. More specifically:
* <ul>
* <li>if ultrapeerHeader==null, returns true if this has space for an
* unrouted old-style connection.
* <li>if ultrapeerHeader.equals("true"), returns true if this has slots
* for a leaf connection.
* <li>if ultrapeerHeader.equals("false"), returns true if this has slots
* for an ultrapeer connection.
* </ul>
*
* <tt>useragentHeader</tt> is used to prefer LimeWire and certain trusted
* vendors. <tt>outgoing</tt> is currently unused, but may be used to
* prefer incoming or outgoing connections in the forward.
*
* @param outgoing true if this is an outgoing connection; true if incoming
* @param ultrapeerHeader the value of the X-Ultrapeer header, or null
* if it was not written
* @param useragentHeader the value of the User-Agent header, or null if
* it was not written
* @return true if a connection of the given type is allowed
*/
public HandshakeStatus allowConnection(HandshakeResponse hr, boolean leaf);
/**
* Tells if this node thinks that more ultrapeers are needed on the
* network. This method should be invoked on a ultrapeer only, as
* only ultrapeer may have required information to make informed
* decision.
* @return true, if more ultrapeers needed, false otherwise
*/
public boolean supernodeNeeded();
/**
* Returns a list of this' initialized connections.
*/
public List<RoutedConnection> getInitializedConnections();
/**
* return a list of initialized connection that matches the parameter
* String loc.
* create a new linkedlist to return.
*/
public List<RoutedConnection> getInitializedConnectionsMatchLocale(String loc);
/**
* Returns a list of this' initialized connections.
*/
public List<RoutedConnection> getInitializedClientConnections();
/**
* return a list of initialized client connection that matches the parameter
* String loc.
* create a new linkedlist to return.
*/
public List<RoutedConnection> getInitializedClientConnectionsMatchLocale(String loc);
/**
* @return all of this' connections.
*/
public List<RoutedConnection> getConnections();
/**
* Accessor for the <tt>Set</tt> of push proxies for this node. If
* there are no push proxies available, this will return an empty <tt>Set</tt>.
*
* Callers can take ownership of the returned set; the set might be immutable.
*
* @return a <tt>Set</tt> of push proxies with a maximum size of 4
*
* TODO: should the set of pushproxy UPs be cached and updated as
* connections are killed and created?
*/
public Set<Connectable> getPushProxies();
/**
* Sends a TCPConnectBack request to (up to) 2 connected Ultrapeers.
* @returns false if no requests were sent, otherwise true.
*/
public boolean sendTCPConnectBackRequests();
/**
* Sends a UDPConnectBack request to (up to) 4 (and at least 2)
* connected Ultrapeers.
* @returns false if no requests were sent, otherwise true.
*/
public boolean sendUDPConnectBackRequests(GUID cbGuid);
/**
* Sends a QueryStatusResponse message to as many Ultrapeers as possible.
*
* @param
*/
public void updateQueryStatus(QueryStatusResponse stat);
/**
* Returns the <tt>Endpoint</tt> for an Ultrapeer connected via TCP,
* if available.
*
* @return the <tt>Endpoint</tt> for an Ultrapeer connected via TCP if
* there is one, otherwise returns <tt>null</tt>
*/
public Endpoint getConnectedGUESSUltrapeer();
/** Returns a <tt>List<tt> of Ultrapeers connected via TCP that are GUESS
* enabled.
*
* @return A non-null List of GUESS enabled, TCP connected Ultrapeers. The
* are represented as ManagedConnections.
*/
public List<RoutedConnection> getConnectedGUESSUltrapeers();
/**
* Adds an incoming connection to the list of connections. Note that
* the incoming connection has already been initialized before
* this method is invoked.
* Should only be called from a thread that has this' monitor.
* This is called from initializeExternallyGeneratedConnection, for
* incoming connections
*
* Default access for testing.
*/
void connectionInitializingIncoming(RoutedConnection c);
/**
* Marks a connection fully initialized, but only if that connection wasn't
* removed from the list of open connections during its initialization.
* Should only be called from a thread that has this' monitor.
*
* Default access for testing.
*/
boolean connectionInitialized(RoutedConnection c);
/**
* Iterates over all the connections and sends the updated CapabilitiesVM
* down every one of them.
*/
public void sendUpdatedCapabilities();
/**
* Disconnects from the network. Closes all connections and sets
* the number of connections to zero.
*
* @param willTryToReconnect Whether or not this is only a temporary disconnection
*/
public void disconnect(boolean willTryToReconnect);
/**
* Returns this node's average connection time - in ms - including the current session.
*
*/
public long getCurrentAverageUptime();
/**
* Connects to the network. Ensures the number of messaging connections
* is non-zero and recontacts the pong server as needed.
*/
public void connect();
/**
* Returns true if this can safely switch from Ultrapeer to leaf mode.
* Typically this means that we are an Ultrapeer and have no leaf
* connections.
*
* @return <tt>true</tt> if we will allow ourselves to become a leaf,
* otherwise <tt>false</tt>
*/
public boolean allowLeafDemotion();
/**
* Notifies the connection manager that it should attempt to become an
* Ultrapeer. If we already are an Ultrapeer, this will be ignored.
*
* @param demotionLimit the number of attempts by other Ultrapeers to
* demote us to a leaf that we should allow before giving up in the
* attempt to become an Ultrapeer
*/
public void tryToBecomeAnUltrapeer(int demotionLimit);
/**
* Gets the number of preferred connections to maintain.
*/
public int getPreferredConnectionCount();
/**
* Determines if we're attempting to maintain the idle connection count.
*/
public boolean isConnectionIdle();
/**
* This method notifies the connection manager that the user does not have
* a live connection to the Internet to the best of our determination.
* In this case, we notify the user with a message and maintain any
* Gnutella hosts we have already tried instead of discarding them.
*/
public void noInternetConnection();
/**
* Count how many connections have already received N messages
*/
public int countConnectionsWithNMessages(int messageThreshold);
/**
* Count up all the messages on active connections
*/
public int getActiveConnectionMessages();
/**
* @return true if a connect back request can be sent on the provided network
*/
public boolean canSendConnectBack(Network network);
/**
* notification that a connect back request has been sent on the given network
*/
public void connectBackSent(Network network);
/** Returns the number of connections that are currently being fetched. */
public int getNumFetchingConnections();
}