package com.limegroup.gnutella;
import java.io.File;
import java.io.IOException;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import junit.framework.Test;
import org.apache.http.Header;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicHttpRequest;
import org.limewire.core.settings.ConnectionSettings;
import org.limewire.core.settings.UploadSettings;
import org.limewire.gnutella.tests.LimeTestCase;
import org.limewire.gnutella.tests.LimeTestUtils;
import org.limewire.util.FileUtils;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.limegroup.gnutella.http.HTTPHeaderName;
import com.limegroup.gnutella.http.HttpTestUtils;
import com.limegroup.gnutella.library.FileCollection;
import com.limegroup.gnutella.library.FileDesc;
import com.limegroup.gnutella.library.FileManagerTestUtils;
import com.limegroup.gnutella.library.FileView;
import com.limegroup.gnutella.library.GnutellaFiles;
import com.limegroup.gnutella.library.Library;
/**
* This class tests HTTP requests involving URNs, as specified in HUGE v094,
* utilizing the X-Gnutella-Content-URN header and the
* X-Gnutella-Alternate-Location header.
*/
public final class UrnHttpRequestTest extends LimeTestCase {
private static final String STATUS_503 = "HTTP/1.1 503 Service Unavailable";
private static final String STATUS_404 = "HTTP/1.1 404 Not Found";
private static final String STATUS_400 = "HTTP/1.1 400 Bad Request";
@Inject private Library library;
@Inject @GnutellaFiles private FileView gnutellaFileView;
private HTTPUploadManager uploadManager;
@Inject private LifecycleManager lifeCycleManager;
@Inject private NetworkManager networkManager;
@Inject @GnutellaFiles private FileCollection gnutellaFileCollection;
public UrnHttpRequestTest(String name) {
super(name);
}
public static Test suite() {
return buildTestSuite(UrnHttpRequestTest.class);
}
public static void main(String[] args) {
junit.textui.TestRunner.run(suite());
}
@Override
protected void setUp() throws Exception {
ConnectionSettings.LOCAL_IS_PRIVATE.setValue(false);
// initialize services
Injector injector = LimeTestUtils.createInjector(LimeTestUtils.createModule(this));
uploadManager = (HTTPUploadManager) injector.getInstance(UploadManager.class);
lifeCycleManager.start();
// make sure the FileDesc objects in file manager are up-to-date
FileManagerTestUtils.waitForLoad(library,2000);
// create shared files with random content
Random random = new Random();
for (int i = 0; i < 5; i++) {
byte[] data = new byte[random.nextInt(255) + 1];
random.nextBytes(data);
File file = new File(_scratchDir, "file" + i + ".tmp");
FileUtils.writeObject(file, data);
file.deleteOnExit();
assertNotNull(gnutellaFileCollection.add(file).get(1, TimeUnit.SECONDS));
}
assertGreaterThanOrEquals("FileManager should have loaded files", 5, gnutellaFileCollection.size());
}
@Override
protected void tearDown() throws Exception {
lifeCycleManager.shutdown();
}
/**
* Tests requests that follow the traditional "get" syntax to make sure that
* the X-Gnutella-Content-URN header is always returned.
*/
public void testLimitReachedRequests() throws Exception {
int maxUploads = UploadSettings.HARD_MAX_UPLOADS.getValue();
UploadSettings.HARD_MAX_UPLOADS.setValue(0);
try {
for (FileDesc fd: gnutellaFileView) {
String uri = LimeTestUtils.getRelativeRequest(fd.getSHA1Urn());
BasicHttpRequest request = new BasicHttpRequest("GET", uri,
HttpVersion.HTTP_1_1);
request.addHeader(HTTPHeaderName.GNUTELLA_CONTENT_URN.create(fd
.getSHA1Urn()));
sendRequestThatShouldFail(request, STATUS_503);
// sendRequestThatShouldFail(HTTPRequestMethod.HEAD, request,
// fd,
// STATUS_503);
}
} finally {
UploadSettings.HARD_MAX_UPLOADS.setValue(maxUploads);
}
}
/**
* Test requests by URN.
*/
public void testHttpUrnRequest() throws Exception {
for (FileDesc fd : gnutellaFileView) {
String uri = "/uri-res/N2R?" + fd.getSHA1Urn().httpStringValue();
BasicHttpRequest request = new BasicHttpRequest("GET", uri,
HttpVersion.HTTP_1_1);
sendRequestThatShouldSucceed(request, fd);
request = new BasicHttpRequest("HEAD", uri, HttpVersion.HTTP_1_1);
sendRequestThatShouldSucceed(request, fd);
}
}
/**
* Test requests by URN that came from LimeWire 2.8.6.
* /get/0//uri-res/N2R?urn:sha1:AZUCWY54D63______PHN7VSVTKZA3YYT HTTP/1.1
*/
public void testMalformedHttpUrnRequest() throws Exception {
for (FileDesc fd : gnutellaFileView) {
String uri = "/get/0//uri-res/N2R?"
+ fd.getSHA1Urn().httpStringValue();
BasicHttpRequest request = new BasicHttpRequest("GET", uri,
HttpVersion.HTTP_1_1);
sendRequestThatShouldFail(request, STATUS_400);
request = new BasicHttpRequest("HEAD", uri, HttpVersion.HTTP_1_1);
sendRequestThatShouldFail(request, STATUS_400);
}
}
/**
* Tests requests that follow the traditional "get" syntax to make sure that
* the X-Gnutella-Content-URN header is always returned.
*/
public void testTraditionalGetForReturnedUrn() throws Exception {
for (FileDesc fd : gnutellaFileView) {
String uri = LimeTestUtils.getRelativeRequest(fd.getSHA1Urn());
BasicHttpRequest request = new BasicHttpRequest("GET", uri,
HttpVersion.HTTP_1_1);
request.addHeader(HTTPHeaderName.GNUTELLA_CONTENT_URN.create(fd
.getSHA1Urn()));
sendRequestThatShouldSucceed(request, fd);
request = new BasicHttpRequest("HEAD", uri, HttpVersion.HTTP_1_1);
request.addHeader(HTTPHeaderName.GNUTELLA_CONTENT_URN.create(fd
.getSHA1Urn()));
sendRequestThatShouldSucceed(request, fd);
}
}
/**
* Tests requests that follow the traditional "get" syntax but that also
* include the X-Gnutella-Content-URN header. In these requests, both the
* URN and the file name and index are correct, so a valid result is
* expected.
*/
public void testTraditionalGetWithContentUrn() throws Exception {
for (FileDesc fd : gnutellaFileView) {
String uri = LimeTestUtils.getRelativeRequest(fd.getSHA1Urn());
BasicHttpRequest request = new BasicHttpRequest("GET", uri,
HttpVersion.HTTP_1_1);
sendRequestThatShouldSucceed(request, fd);
request = new BasicHttpRequest("HEAD", uri, HttpVersion.HTTP_1_1);
sendRequestThatShouldSucceed(request, fd);
}
}
/**
* Tests get requests that follow the traditional Gnutella get format and
* that include an invalid content URN header -- these should fail with
* error code 404.
*/
public void testTraditionalGetWithInvalidContentUrn() throws Exception {
for (FileDesc fd : gnutellaFileView) {
String uri = LimeTestUtils.getRelativeRequest(fd.getSHA1Urn());
BasicHttpRequest request = new BasicHttpRequest("GET", uri,
HttpVersion.HTTP_1_1);
request.addHeader(HTTPHeaderName.GNUTELLA_CONTENT_URN
.create("urn:sha1:PLSTHIPQGSSZTS5FJUPAKUZWUGYQYPFB"));
sendRequestThatShouldFail(request, STATUS_404);
request = new BasicHttpRequest("HEAD", uri, HttpVersion.HTTP_1_1);
request.addHeader(HTTPHeaderName.GNUTELLA_CONTENT_URN
.create("urn:sha1:PLSTHIPQGSSZTS5FJUPAKUZWUGYQYPFB"));
sendRequestThatShouldFail(request, STATUS_404);
}
}
/**
* Tests to make sure that invalid traditional Gnutella get requests with
* matching X-Gnutella-Content-URN header values also fail with 404.
*/
public void testInvalidTraditionalGetWithValidContentUrn() throws Exception {
for (FileDesc fd : gnutellaFileView) {
String uri = LimeTestUtils.getRelativeRequest(fd.getSHA1Urn());
uri = uri.substring(0, uri.length() - 2)+ "xx";
BasicHttpRequest request = new BasicHttpRequest("GET", uri,
HttpVersion.HTTP_1_1);
sendRequestThatShouldFail(request, STATUS_404);
request = new BasicHttpRequest("HEAD", uri, HttpVersion.HTTP_1_1);
sendRequestThatShouldFail(request, STATUS_404);
}
}
/**
* Sends an HTTP request that should succeed and send back all of the
* expected headers.
*/
private void sendRequestThatShouldSucceed(HttpRequest request, FileDesc fd)
throws Exception {
HttpResponse response = getResponse(request);
assertEquals(200, response.getStatusLine().getStatusCode());
// clean up any created uploaders
uploadManager.cleanup();
boolean contentUrnHeaderPresent = false;
Header[] headers = response.getAllHeaders();
assertTrue("HTTP response headers should be present: " + fd,
headers.length > 0);
for (Header header : headers) {
String curString = header.toString();
if (HTTPHeaderName.ALT_LOCATION.matchesStartOfString(curString)) {
continue;
} else if (HTTPHeaderName.GNUTELLA_CONTENT_URN
.matchesStartOfString(curString)) {
URN curUrn = null;
try {
String tmpString = HttpTestUtils.extractHeaderValue(curString);
curUrn = URN.createSHA1Urn(tmpString);
} catch (IOException e) {
assertTrue("unexpected exception: " + e, false);
}
assertEquals(HTTPHeaderName.GNUTELLA_CONTENT_URN.toString()
+ "s should be equal for " + fd, fd.getSHA1Urn(),
curUrn);
contentUrnHeaderPresent = true;
} else if (HTTPHeaderName.CONTENT_RANGE
.matchesStartOfString(curString)) {
continue;
} else if (HTTPHeaderName.CONTENT_TYPE
.matchesStartOfString(curString)) {
continue;
} else if (HTTPHeaderName.CONTENT_LENGTH
.matchesStartOfString(curString)) {
String value = HttpTestUtils.extractHeaderValue(curString);
assertEquals("sizes should match for " + fd, (int) fd
.getFileSize(), Integer.parseInt(value));
} else if (HTTPHeaderName.SERVER.matchesStartOfString(curString)) {
continue;
}
}
assertTrue("content URN header should always be reported:\r\n" + fd
+ "\r\n" + "reply: " + response, contentUrnHeaderPresent);
}
/**
* Sends an HTTP request that should fail if everything is working
* correctly.
*/
private void sendRequestThatShouldFail(HttpRequest request, String error)
throws Exception {
HttpResponse response = getResponse(request);
assertEquals("unexpected HTTP response", error, getStatusLine(response));
}
private String getStatusLine(HttpResponse response) {
return response.getProtocolVersion() + " "
+ response.getStatusLine().getStatusCode() + " "
+ response.getStatusLine().getReasonPhrase();
}
private HttpResponse getResponse(HttpRequest request) throws HttpException, IOException, InterruptedException {
HttpClient client = new DefaultHttpClient();
return client.execute(new HttpHost("127.0.0.1", networkManager.getPort(), "http"), request);
}
}