package com.limegroup.gnutella.downloader;
import java.io.IOException;
import java.net.URL;
import java.util.Iterator;
import java.util.Set;
import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.impl.client.DefaultHttpClient;
import com.limegroup.gnutella.ErrorService;
import com.limegroup.gnutella.RemoteFileDesc;
import com.limegroup.gnutella.URN;
import com.limegroup.gnutella.altlocs.AlternateLocationCollection;
import com.limegroup.gnutella.altlocs.AlternateLocationCollector;
import com.limegroup.gnutella.http.HTTPHeaderName;
import com.limegroup.gnutella.messages.QueryReply;
import com.limegroup.gnutella.util.CommonUtils;
/**
* This class handles sending HTTP HEAD requests to alternate locations,
* propagating the download "mesh" of alternate locations for files.
*/
final class HeadRequester implements Runnable {
/**
* The <tt>Set</tt> of hosts to send HEAD requests to.
*/
private final Set HOSTS;
/**
* The <tt>URN</tt> instance to propagate through the mesh.
*/
private final URN RESOURCE_NAME;
/**
* The <tt>AlternateLocationCollector</tt> to notify of any new
* alternate locations that are discovered while making HEAD
* requests.
*/
private final AlternateLocationCollector COLLECTOR;
/**
* The total collection of all new alternate locations for the
* file.
*/
private final AlternateLocationCollection TOTAL_ALTS;
/**
* Constructs a new <tt>HeadRequester</tt> instance for the specified
* <tt>List</tt> of hosts to be notified, the <tt>URN</tt> to
* propagate, the <tt>AlternateLocationCollector</tt> to store newly
* discovered locations, and the list of alternate locations to report.
*
* @param uploaders the hosts to send HEAD requests to
* @param resourceName the <tt>URN</tt> of the resource
* @param collector the <tt>AlternateLocationCollector</tt> that will
* store any newly discovered locations
* @param totalAlts the total known alternate locations to report
*/
public HeadRequester(Set hosts,
URN resourceName,
AlternateLocationCollector collector,
AlternateLocationCollection totalAlts) {
HOSTS = hosts;
RESOURCE_NAME = resourceName;
COLLECTOR = collector;
TOTAL_ALTS = totalAlts;
}
/**
* Implements <tt>Runnable</tt>.<p>
*
* Performs HEAD requests on the <tt>List</tt> of hosts to propagate the
* download mesh.
*/
public void run() {
try {
Iterator iter = HOSTS.iterator();
while(iter.hasNext()) {
RemoteFileDesc rfd = (RemoteFileDesc)iter.next();
if(QueryReply.isFirewalledQuality(rfd.getQuality())) {
// do not attempt to make a HEAD request to firewalled hosts
continue;
}
URN urn = rfd.getSHA1Urn();
if(urn == null) continue;
if(!urn.equals(RESOURCE_NAME)) continue;
URL url = rfd.getUrl();
String connectTo = url.toExternalForm();
HttpHead head = new HttpHead(connectTo);
head.addHeader("User-Agent",
CommonUtils.getHttpServer());
head.addHeader("Cache-Control", "no-cache");
head.addHeader(
HTTPHeaderName.GNUTELLA_CONTENT_URN.httpStringValue(),
RESOURCE_NAME.httpStringValue());
head.addHeader(
HTTPHeaderName.ALT_LOCATION.httpStringValue(),
TOTAL_ALTS.httpStringValue());
head.addHeader(
HTTPHeaderName.CONNECTION.httpStringValue(),
"close");
//head.setFollowRedirects(false);
DefaultHttpClient client = new DefaultHttpClient();
try {
HttpResponse res = client.execute(head);
String contentUrn = getHeader(res, HTTPHeaderName.GNUTELLA_CONTENT_URN_STR);
if(contentUrn == null)
continue;
try {
URN reportedUrn = URN.createSHA1Urn(contentUrn);
if(!reportedUrn.equals(RESOURCE_NAME)) {
continue;
}
} catch(IOException e) {
continue;
}
String altLocs = getHeader(res, HTTPHeaderName.ALT_LOCATION_STR);
if(altLocs == null)
continue;
AlternateLocationCollection alc =
AlternateLocationCollection.createCollectionFromHttpValue(altLocs);
if (alc == null)
continue;
if(alc.getSHA1Urn().equals(COLLECTOR.getSHA1Urn()))
COLLECTOR.addAll(alc);
} catch(IOException e) {
continue;
}
}
} catch(Throwable e) {
ErrorService.error(e);
}
}
/**
* Simple helper method to retrieve a header from an HttpMethod.
*/
private static String getHeader(HttpResponse methid, String name) {
Header header = methid.getFirstHeader(name);
if(header == null)
return null;
else
return header.getValue();
}
}