Implemented Httpbin server for HttpDownloader testing
gzip not working yet
This commit is contained in:
parent
4f8fbf9ed9
commit
6266f5b298
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue