package com.limegroup.gnutella.http;
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Locale;
import java.util.StringTokenizer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.Header;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.protocol.HttpContext;
import org.limewire.http.HeaderInterceptor;
import org.limewire.io.NetworkUtils;
import com.limegroup.gnutella.URN;
import com.limegroup.gnutella.uploader.HTTPUploader;
/**
* Processes Gnutella headers from an {@link HttpRequest} and updates
* a corresponding {@link HTTPUploader}.
*/
public class FeatureHeaderInterceptor implements HeaderInterceptor {
private static final Log LOG = LogFactory.getLog(FeatureHeaderInterceptor.class);
private HTTPUploader uploader;
public FeatureHeaderInterceptor(HTTPUploader uploader) {
this.uploader = uploader;
}
public void process(Header header, HttpContext context)
throws HttpException, IOException {
if (readContentURNHeader(header))
;
else if (readQueueVersion(header))
;
else if (readFeatureHeader(header))
;
else if (readXDownloadedHeader(header))
;
else if (readNodeHeader(header))
;
}
/**
* Look for X-Downloaded header which represents number of bytes for this
* file already downloaded by peer
*
* @return true if it had a X-Downloaded header
*/
private boolean readXDownloadedHeader(Header header) {
if (!HTTPHeaderName.DOWNLOADED.matches(header))
return false;
try {
uploader.setTotalAmountUploadedBefore(Integer.parseInt(header.getValue()));
} catch (NumberFormatException e) {
}
return true;
}
/**
* This method parses the "X-Gnutella-Content-URN" header, as specified
* in HUGE v0.93. This assigns the requested urn value for this
* upload, which otherwise remains null.
*
* @param contentUrnStr the string containing the header
* @return a new <tt>URN</tt> instance for the request line, or
* <tt>null</tt> if there was any problem creating it
*
* @return true if the header had a contentURN field
*/
private boolean readContentURNHeader(Header header) {
if (!HTTPHeaderName.GNUTELLA_CONTENT_URN.matches(header))
return false;
try {
uploader.setRequestedURN(URN.createSHA1Urn(header.getValue()));
} catch(IOException e) {
uploader.setRequestedURN(URN.INVALID);
}
return true;
}
private boolean readQueueVersion(Header header) {
if (!HTTPHeaderName.QUEUE.matches(header))
return false;
// we are not interested in the value at this point, the fact that the
// header was sent implies that the uploader supports queueing.
uploader.setSupportsQueueing(true);
return true;
}
/**
* Reads the X-Features header
*
* @return true if the header had an node description value
*/
private boolean readFeatureHeader(Header header) {
if (!HTTPHeaderName.FEATURES.matches(header))
return false;
String value = header.getValue();
if (LOG.isDebugEnabled())
LOG.debug("reading feature header: " + value);
StringTokenizer tok = new StringTokenizer(value, ",");
while (tok.hasMoreTokens()) {
String feature = tok.nextToken();
String protocol = "";
int slash = feature.indexOf("/");
if (slash == -1) {
protocol = feature.toLowerCase(Locale.US).trim();
} else {
protocol = feature.substring(0, slash).toLowerCase(Locale.US).trim();
}
// not interested in the version ...
if (protocol.equals(HTTPConstants.BROWSE_PROTOCOL))
uploader.setBrowseHostEnabled(true);
else if (protocol.equals(HTTPConstants.QUEUE_PROTOCOL))
uploader.setSupportsQueueing(true);
else if (protocol.equals(HTTPConstants.PUSH_LOCS))
uploader.getAltLocTracker().setWantsFAlts(true);
else if (protocol.equals(HTTPConstants.FW_TRANSFER)) {
try {
// for this header we care about the version
uploader.getAltLocTracker().setFwtVersion((int) HTTPUtils.parseFeatureToken(feature));
uploader.getAltLocTracker().setWantsFAlts(true);
} catch (ProblemReadingHeaderException prhe) {
continue;
}
}
}
return true;
}
/**
* Reads the X-Node header.
*
* @return true if the header had an node description value
*/
private boolean readNodeHeader(final Header header) {
if (!HTTPHeaderName.NODE.matches(header))
return false;
setHostAndPort(header.getValue());
return true;
}
/**
* Sets the host and port of <code>uploader</code> from <code>value</code>
* if it describes a valid address.
*
* @param value host:port
* @return true, if host and port were set
*/
private boolean setHostAndPort(String value) {
InetAddress host;
int port = -1;
StringTokenizer st = new StringTokenizer(value, ":");
if (st.countTokens() == 2) {
try {
host = InetAddress.getByName(st.nextToken().trim());
port = Integer.parseInt(st.nextToken().trim());
if (NetworkUtils.isValidPort(port)) {
uploader.setHost(host.getHostAddress());
uploader.setGnutellaPort(port);
return true;
}
} catch (UnknownHostException ignore) {
} catch (NumberFormatException ignore) {
}
}
return false;
}
}