package com.limegroup.gnutella.uploader;
import java.io.IOException;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.nio.entity.ConsumingNHttpEntity;
import org.apache.http.nio.entity.NStringEntity;
import org.apache.http.nio.protocol.SimpleNHttpRequestHandler;
import org.apache.http.protocol.HttpContext;
import org.limewire.http.auth.ServerAuthState;
import com.limegroup.gnutella.URN;
import com.limegroup.gnutella.Uploader.UploadStatus;
import com.limegroup.gnutella.altlocs.AltLocManager;
import com.limegroup.gnutella.http.HTTPHeaderName;
import com.limegroup.gnutella.library.FileDesc;
/**
* Responds with an HTTP 503 error signaling that the limit of allowed uploads
* has been reached.
*/
public class LimitReachedRequestHandler extends SimpleNHttpRequestHandler {
/** Time to wait for a retry-after because we're validating the file.*/
public static final String RETRY_AFTER_VALIDATING = 20 + "";
/**
* The time to wait for a normal retry after in minutes.
*/
public static final int RETRY_AFTER_TIME = 60 * 15;
/**
* Number of seconds the remote host should wait before retrying in case we
* don't have any alt-locs left to send. 20 minutes.
*/
private static final String NO_ALT_LOCS_RETRY_AFTER = "" + (20 * 60);
private static final String FRIEND_NO_ALT_LOCS_RETRY_AFTER = String.valueOf(30);
/**
* Number of seconds the remote host should wait before retrying in case we
* still have alt-locs left to send. (15 minute).
*/
private static final String NORMAL_RETRY_AFTER = "" + RETRY_AFTER_TIME;
/**
* The error message to send in the message body.
*/
private static final String ERROR_MESSAGE = "Server busy. Too many active uploads.";
/** Error msg to use when validating. */
private static final String VALIDATING_MSG = "Validating file. One moment please.";
/** True if this is a LimitReached state because we're validating the file. */
private final boolean validating;
private final HTTPUploader uploader;
private final FileDesc fd;
private final HTTPHeaderUtils httpHeaderUtils;
private final AltLocManager altLocManager;
/**
* Creates a new <tt>LimitReachedUploadState</tt> with the specified
* <tt>FileDesc</tt>.
*/
LimitReachedRequestHandler(HTTPUploader uploader, HTTPHeaderUtils httpHeaderUtils, AltLocManager altLocManager) {
this.uploader = uploader;
this.validating = false; //Note: Never invoked with validating = true, see Uploader.NOT_VALIDATED.
this.fd = uploader.getFileDesc();
this.httpHeaderUtils = httpHeaderUtils;
this.altLocManager = altLocManager;
}
public ConsumingNHttpEntity entityRequest(HttpEntityEnclosingRequest request,
HttpContext context) throws HttpException, IOException {
return null;
}
@Override
public void handle(HttpRequest request, HttpResponse response,
HttpContext context) throws HttpException, IOException {
httpHeaderUtils.addProxyHeader(response);
httpHeaderUtils.addAltLocationsHeader(response, uploader.getAltLocTracker(), altLocManager);
String errorMsg = ERROR_MESSAGE;
if (fd != null) {
URN sha1 = fd.getSHA1Urn();
if (validating) {
errorMsg = VALIDATING_MSG;
response.addHeader(HTTPHeaderName.RETRY_AFTER
.create(RETRY_AFTER_VALIDATING));
} else if (sha1 != null) {
// write the Retry-After header, using different values
// depending on if we had any alts to send or not.
String retry = !altLocManager.hasAltlocs(sha1) || isFriendRequest(context) ? getRetryAfterNoAltLocs(context)
: NORMAL_RETRY_AFTER;
response.addHeader(HTTPHeaderName.RETRY_AFTER.create(retry));
httpHeaderUtils.addRangeHeader(response, uploader, fd);
} else {
response.addHeader(HTTPHeaderName.RETRY_AFTER.create(getRetryAfterNoAltLocs(context)));
}
}
uploader.setState(UploadStatus.LIMIT_REACHED);
response.setStatusCode(HttpStatus.SC_SERVICE_UNAVAILABLE);
response.setEntity(new NStringEntity(errorMsg));
}
private boolean isFriendRequest(HttpContext context) {
// it's enough to check if auth state is there, the guarding handler
// already verified the credentials
ServerAuthState serverAuthState = (ServerAuthState) context.getAttribute(ServerAuthState.AUTH_STATE);
return serverAuthState != null && serverAuthState.getCredentials() != null;
}
/**
* Returns smaller Retry-After value for authenticated friend downloads.
*/
// TODO breaks knowledge about friend downloads, this layer should not
// be aware of friends stuff
private String getRetryAfterNoAltLocs(HttpContext context) {
// it's enough to check if auth state is there, the guarding handler
// already verified the credentials
if (isFriendRequest(context)) {
return FRIEND_NO_ALT_LOCS_RETRY_AFTER;
} else {
return NO_ALT_LOCS_RETRY_AFTER;
}
}
}