package com.limegroup.gnutella.handshaking;
import java.util.Properties;
import org.limewire.io.IpPort;
/**
* A very simple responder to be used by ultrapeers during the connection
* handshake while accepting incoming connections.
*/
public class UltrapeerHandshakeResponder extends DefaultHandshakeResponder {
private final HeadersFactory headersFactory;
private final HandshakeServices handshakeServices;
/**
* Creates a new instance of ClientHandshakeResponder
*
* @param host the host with whom we are handshaking
*/
UltrapeerHandshakeResponder(String host, HeadersFactory headersFactory,
HandshakeServices handshakeServices) {
super(host);
this.handshakeServices = handshakeServices;
this.headersFactory = headersFactory;
}
/**
* Respond to an outgoing connection request.
*
* @param response the headers read from the connection
*/
@Override
protected HandshakeResponse respondToOutgoing(HandshakeResponse response) {
// Outgoing connection.
// If our slots are full, reject it.
HandshakeStatus status = handshakeServices.getHandshakeStatusForResponse(response);
if (!status.isAcceptable()) {
return HandshakeResponse.createRejectOutgoingResponse(status);
}
Properties ret = new Properties();
// They might be giving us guidance
// (We don't give them guidance for outgoing)
if (response.hasLeafGuidance()) {
// Become a leaf if its a good ultrapeer & we can do it.
if (handshakeServices.isLeafDemotionAllowed() && response.isGoodUltrapeer()) {
ret.put(HeaderNames.X_ULTRAPEER, "False");
}
}
// deflate if we can ...
if (response.isDeflateAccepted()) {
ret.put(HeaderNames.CONTENT_ENCODING, HeaderNames.DEFLATE_VALUE);
}
// accept the response
return HandshakeResponse.createAcceptOutgoingResponse(ret);
}
/**
* Respond to an incoming connection request.
*
* @param response the headers read from the connection
*/
@Override
protected HandshakeResponse respondToIncoming(HandshakeResponse response) {
// if this is a connections from the crawler, return the special crawler
// response
if (response.isCrawler()) {
return HandshakeResponse.createCrawlerResponse(handshakeServices);
}
// Incoming connection....
Properties ret = headersFactory.createUltrapeerHeaders(getRemoteIP());
// give own IP address
IpPort localIp = handshakeServices.getLocalIpPort();
ret.put(HeaderNames.LISTEN_IP, localIp.getAddress() + ":" + localIp.getPort());
// Decide whether to allow or reject. Somewhat complicated because
// of ultrapeer guidance.
HandshakeStatus status = reject(response, ret);
if (!status.isAcceptable()) {
// reject the connection, and let the other node know about
// any Ultrapeers we're connected to
return HandshakeResponse.createUltrapeerRejectIncomingResponse(response, status,
handshakeServices);
}
// We do this last, to prevent reject connections from being deflated,
// which may actually increase the amount of bandwidth needed.
if (response.isDeflateAccepted()) {
ret.put(HeaderNames.CONTENT_ENCODING, HeaderNames.DEFLATE_VALUE);
}
// accept the connection, and let the connecting node know about
// Ultrapeers that are as many hops away as possible, to avoid
// cycles.
return HandshakeResponse.createAcceptIncomingResponse(response, ret, handshakeServices);
}
/**
* Returns a HandshakeStatus to be used for rejecting.
*/
private HandshakeStatus reject(HandshakeResponse response, Properties ret) {
// See if this connection can be allowed as a leaf.
HandshakeStatus leafStatus = handshakeServices
.getHandshakeStatusForResponseAsLeaf(response);
boolean allowedAsLeaf = leafStatus.isAcceptable();
// If the user wasn't an ultrapeer, accept or reject
// based on whether or not it was allowed.
// This is because leaf connections cannot upgrade to ultrapeers,
// so the allowAsLeaf was the final check.
if (response.isLeaf()) {
return leafStatus;
}
// Otherwise (if the user is an ultrapeer), there are a few things...
boolean supernodeNeeded = handshakeServices.isUltrapeerNeeded();
// If we can accept them and we don't need more supernodes,
// guide them to become a leaf
if (allowedAsLeaf && !supernodeNeeded) {
ret.put(HeaderNames.X_ULTRAPEER_NEEDED, Boolean.FALSE.toString());
return HandshakeStatus.OK;
}
HandshakeStatus upStatus = handshakeServices.getHandshakeStatusForResponse(response);
boolean allowedAsUltrapeer = upStatus.isAcceptable();
// If supernode is needed or we can't accept them as a leaf,
// see if we can accept them as a supernode.
if (allowedAsUltrapeer) {
// not strictly necessary ...
ret.put(HeaderNames.X_ULTRAPEER_NEEDED, Boolean.TRUE.toString());
return upStatus;
}
// In all other cases, we must reject the connection.
// These are:
// 1) !allowedAsLeaf && !allowedAsUltrapeer
// 2) supernodeNeeded && !alloweedAsUltrapeer
// The reasoning behind 1) is that we cannot accept them as a either a
// leaf or an ultrapeer, so we must reject.
// The reasoning behind 2) is that the network needs a supernode, but
// we are currently unable to service that need, so we must reject.
// Theoretically, it is possible to allow them as a leaf even if
// a supernode was needed, but that would lower the amount of
// well-connected supernodes, ultimately hurting the network.
// This means that the last 10% of leaf slots will always be reserved
// for connections that are unable to be ultrapeers.
if (!allowedAsLeaf) {
return leafStatus;
} else {
return upStatus;
}
}
}