Implemented Httpbin server for HttpDownloader testing

gzip not working yet
This commit is contained in:
daniel oeh 2014-05-18 11:56:15 +02:00
parent 4f8fbf9ed9
commit 6266f5b298
3 changed files with 1659 additions and 23 deletions

View File

@ -1,17 +1,18 @@
package instrumentationTest.de.test.antennapod.service.download;
import android.test.InstrumentationTestCase;
import android.util.Log;
import de.danoeh.antennapod.feed.FeedFile;
import de.danoeh.antennapod.service.download.DownloadRequest;
import de.danoeh.antennapod.service.download.DownloadStatus;
import de.danoeh.antennapod.service.download.Downloader;
import de.danoeh.antennapod.service.download.HttpDownloader;
import de.danoeh.antennapod.util.DownloadError;
import instrumentationTest.de.test.antennapod.util.service.download.HTTPBin;
import java.io.File;
import java.io.IOException;
import android.test.InstrumentationTestCase;
import de.danoeh.antennapod.feed.FeedFile;
import de.danoeh.antennapod.service.download.*;
import android.test.AndroidTestCase;
import android.util.Log;
import de.danoeh.antennapod.util.DownloadError;
import org.apache.commons.io.FileUtils;
public class HttpDownloaderTest extends InstrumentationTestCase {
private static final String TAG = "HttpDownloaderTest";
private static final String DOWNLOAD_DIR = "testdownloads";
@ -20,6 +21,8 @@ public class HttpDownloaderTest extends InstrumentationTestCase {
private File destDir;
private HTTPBin httpServer;
private static final String BASE_URL = "http://127.0.0.1:" + HTTPBin.PORT;
public HttpDownloaderTest() {
super();
@ -32,6 +35,8 @@ public class HttpDownloaderTest extends InstrumentationTestCase {
for (File f : contents) {
assertTrue(f.delete());
}
httpServer.stop();
}
@Override
@ -40,6 +45,8 @@ public class HttpDownloaderTest extends InstrumentationTestCase {
destDir = getInstrumentation().getTargetContext().getExternalFilesDir(DOWNLOAD_DIR);
assertNotNull(destDir);
assertTrue(destDir.exists());
httpServer = new HTTPBin(HTTPBin.PORT);
httpServer.start();
}
private FeedFileImpl setupFeedFile(String downloadUrl, String title, boolean deleteExisting) {
@ -72,23 +79,15 @@ public class HttpDownloaderTest extends InstrumentationTestCase {
}
private static final String URL_404 = "http://httpbin.org/status/404";
private static final String URL_AUTH = "http://httpbin.org/basic-auth/user/passwd";
private static final String URL_404 = BASE_URL + "/status/404";
private static final String URL_AUTH = BASE_URL + "/basic-auth/user/passwd";
public void testPassingHttp() {
download("http://httpbin.org/status/200", "test200", true);
}
public void testPassingHttps() {
download("https://httpbin.org/status/200", "test200", true);
download(BASE_URL + "/status/200", "test200", true);
}
public void testRedirect() {
download("http://httpbin.org/redirect/4", "testRedirect", true);
}
public void testRelativeRedirect() {
download("http://httpbin.org/relative-redirect/4", "testRelativeRedirect", true);
download(BASE_URL + "/redirect/4", "testRedirect", true);
}
public void testGzip() {
@ -100,7 +99,7 @@ public class HttpDownloaderTest extends InstrumentationTestCase {
}
public void testCancel() {
final String url = "http://httpbin.org/delay/3";
final String url = BASE_URL + "/delay/3";
FeedFileImpl feedFile = setupFeedFile(url, "delay", true);
final Downloader downloader = new HttpDownloader(new DownloadRequest(feedFile.getFile_url(), url, "delay", 0, feedFile.getTypeAsInt()));
Thread t = new Thread() {
@ -137,7 +136,7 @@ public class HttpDownloaderTest extends InstrumentationTestCase {
assertTrue(new File(downloader.getDownloadRequest().getDestination()).exists());
}
public void testAuthenticationShouldSucceed() {
public void testAuthenticationShouldSucceed() throws InterruptedException {
download(URL_AUTH, "testAuthSuccess", true, true, "user", "passwd", true);
}

View File

@ -0,0 +1,217 @@
package instrumentationTest.de.test.antennapod.util.service.download;
import android.util.Base64;
import android.util.Log;
import de.danoeh.antennapod.BuildConfig;
import java.io.*;
import java.util.Arrays;
import java.util.Map;
import java.util.Random;
import java.util.zip.GZIPOutputStream;
/**
* Http server for testing purposes
* <p/>
* Supported features:
* <p/>
* /status/code: Returns HTTP response with the given status code
* /redirect/n: Redirects n times
* /delay/n: Delay response for n seconds
* /basic-auth/username/password: Basic auth with username and password
* /gzip/n: Send gzipped data of size n bytes
* /files/id: Accesses the file with the specified ID (this has to be added first via serveFile).
*/
public class HTTPBin extends NanoHTTPD {
private static final String TAG = "HTTPBin";
public static final int PORT = 8124;
private static final String MIME_HTML = "text/html";
private static final String MIME_PLAIN = "text/plain";
public HTTPBin(int port) {
super(port);
}
public HTTPBin() {
super(PORT);
}
@Override
public Response serve(IHTTPSession session) {
if (BuildConfig.DEBUG) Log.d(TAG, "Requested url: " + session.getUri());
String[] segments = session.getUri().split("/");
if (segments.length < 3) {
Log.w(TAG, String.format("Invalid number of URI segments: %d %s", segments.length, Arrays.toString(segments)));
get404Error();
}
final String func = segments[1];
final String param = segments[2];
final Map<String, String> headers = session.getHeaders();
if (func.equalsIgnoreCase("status")) {
try {
int code = Integer.parseInt(param);
return getStatus(code);
} catch (NumberFormatException e) {
e.printStackTrace();
return getInternalError();
}
} else if (func.equalsIgnoreCase("redirect")) {
try {
int times = Integer.parseInt(param);
if (times < 0) {
throw new NumberFormatException("times <= 0: " + times);
}
return getRedirectResponse(times - 1);
} catch (NumberFormatException e) {
e.printStackTrace();
return getInternalError();
}
} else if (func.equalsIgnoreCase("delay")) {
try {
int sec = Integer.parseInt(param);
if (sec <= 0) {
throw new NumberFormatException("sec <= 0: " + sec);
}
Thread.sleep(sec * 1000L);
return getOKResponse();
} catch (NumberFormatException e) {
e.printStackTrace();
return getInternalError();
} catch (InterruptedException e) {
e.printStackTrace();
return getInternalError();
}
} else if (func.equalsIgnoreCase("basic-auth")) {
if (!headers.containsKey("authorization")) {
Log.w(TAG, "No credentials provided");
return getUnauthorizedResponse();
}
try {
String credentials = new String(Base64.decode(headers.get("authorization").split(" ")[1], 0), "UTF-8");
String[] credentialParts = credentials.split(":");
if (credentialParts.length != 2) {
Log.w(TAG, "Unable to split credentials: " + Arrays.toString(credentialParts));
return getInternalError();
}
if (credentialParts[0].equals(segments[2])
&& credentialParts[1].equals(segments[3])) {
Log.i(TAG, "Credentials accepted");
return getOKResponse();
} else {
Log.w(TAG, String.format("Invalid credentials. Expected %s, %s, but was %s, %s",
segments[2], segments[3], credentialParts[0], credentialParts[1]));
return getUnauthorizedResponse();
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return getInternalError();
}
} else if (func.equalsIgnoreCase("gzip")) {
try {
int size = Integer.parseInt(param);
if (size <= 0) {
Log.w(TAG, "Invalid size for gzipped data: " + size);
throw new NumberFormatException();
}
return getGzippedResponse(size);
} catch (NumberFormatException e) {
e.printStackTrace();
return getInternalError();
} catch (IOException e) {
e.printStackTrace();
return getInternalError();
}
}
return get404Error();
}
private Response getGzippedResponse(int size) throws IOException {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
final byte[] buffer = new byte[size];
Random random = new Random(System.currentTimeMillis());
random.nextBytes(buffer);
ByteArrayOutputStream compressed = new ByteArrayOutputStream();
GZIPOutputStream gzipOutputStream = new GZIPOutputStream(compressed);
gzipOutputStream.write(buffer);
InputStream inputStream = new ByteArrayInputStream(compressed.toByteArray());
Response response = new Response(Response.Status.OK, MIME_PLAIN, inputStream);
response.addHeader("Content-encoding", "gzip");
response.addHeader("Content-length", String.valueOf(compressed.size()));
return response;
}
private Response getStatus(final int code) {
Response.IStatus status = (code == 200) ? Response.Status.OK :
(code == 201) ? Response.Status.CREATED :
(code == 206) ? Response.Status.PARTIAL_CONTENT :
(code == 301) ? Response.Status.REDIRECT :
(code == 304) ? Response.Status.NOT_MODIFIED :
(code == 400) ? Response.Status.BAD_REQUEST :
(code == 401) ? Response.Status.UNAUTHORIZED :
(code == 403) ? Response.Status.FORBIDDEN :
(code == 404) ? Response.Status.NOT_FOUND :
(code == 405) ? Response.Status.METHOD_NOT_ALLOWED :
(code == 416) ? Response.Status.RANGE_NOT_SATISFIABLE :
(code == 500) ? Response.Status.INTERNAL_ERROR : new Response.IStatus() {
@Override
public int getRequestStatus() {
return code;
}
@Override
public String getDescription() {
return "Unknown";
}
};
return new Response(status, MIME_HTML, "");
}
private Response getRedirectResponse(int times) {
if (times > 0) {
Response response = new Response(Response.Status.REDIRECT, MIME_HTML, "This resource has been moved permanently");
response.addHeader("Location", "/redirect/" + times);
return response;
} else if (times == 0) {
return getOKResponse();
} else {
return getInternalError();
}
}
private Response getUnauthorizedResponse() {
Response response = new Response(Response.Status.UNAUTHORIZED, MIME_HTML, "");
response.addHeader("WWW-Authenticate", "Basic realm=\"Test Realm\"");
return response;
}
private Response getOKResponse() {
return new Response(Response.Status.OK, MIME_HTML, "");
}
private Response getInternalError() {
return new Response(Response.Status.INTERNAL_ERROR, MIME_HTML, "The server encountered an internal error");
}
private Response get404Error() {
return new Response(Response.Status.NOT_FOUND, MIME_HTML, "The requested URL was not found on this server");
}
}

File diff suppressed because it is too large Load Diff