/* The MIT License (MIT)
* Copyright (c) 2014 Nicholas Wright
* http://opensource.org/licenses/MIT
*/
package com.github.dozedoff.commonj.net;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
public class JettyHttpClientTest {
static final int SERVER_PORT = 5400;
static final int TEST_DATA_SIZE = 25;
static final int READ_TIMEOUT_CLASS = 100;
static final int READ_TIMEOUT_TEST = 1500;
private static enum Pages {
data, range, wait, notok, error, agent
};
JettyHttpClient cut;
static Server server;
static TestHandler testHandler;
HashMap<Pages, URL> pageURLs = new HashMap<>();
@BeforeClass
public static void startServer() throws Exception {
server = new Server(SERVER_PORT);
testHandler = new TestHandler();
server.setHandler(testHandler);
server.start();
}
@AfterClass
public static void stopServer() throws Exception {
server.stop();
}
@Before
public void setUp() throws Exception {
cut = new JettyHttpClient();
testHandler.setTestData(new byte[0]);
server.start();
}
@Test(expected=IllegalArgumentException.class)
public void testSetTimeoutInvalid() {
cut.setTimeout(-100, TimeUnit.MILLISECONDS);
}
@Test
public void testSetTimeoutZero() {
cut.setTimeout(0, TimeUnit.MILLISECONDS);
}
@Test
public void testSetTimeoutPositive() {
cut.setTimeout(10, TimeUnit.SECONDS);
}
@Test
public void testGetData() throws Exception {
testHandler.setTestData(generateRandomData(TEST_DATA_SIZE));
byte[] data = cut.getData(getURL(Pages.data));
assertThat(data, is(testHandler.getTestData()));
}
@Test(timeout = READ_TIMEOUT_TEST, expected = TimeoutException.class)
public void testGetDataTimeout() throws Exception {
cut.setTimeout(READ_TIMEOUT_CLASS, TimeUnit.MILLISECONDS);
cut.getData(getURL(Pages.wait));
}
@Test(expected=PageLoadException.class)
public void testGetDataBadRequest() throws Exception {
cut.getData(getURL(Pages.notok));
}
@Test(expected=IOException.class)
public void testGetDataIOExceptiom() throws Exception {
cut.getData(getURL(Pages.error));
}
@Test
public void testGetLenght() throws Exception {
testHandler.setTestData(generateRandomData(TEST_DATA_SIZE));
Long size = cut.getLenght(getURL(Pages.data));
assertThat(size, is((long) testHandler.getTestData().length));
}
@Test(timeout = READ_TIMEOUT_TEST, expected = TimeoutException.class)
public void testGetLenghtTimeOut() throws Exception {
cut.setTimeout(READ_TIMEOUT_CLASS, TimeUnit.MILLISECONDS);
cut.getLenght(getURL(Pages.wait));
}
@Test
public void testGetHeader() throws Exception {
int dataLength = 5000;
testHandler.setTestData(generateRandomData(dataLength));
Map<String, List<String>> header = cut.getHeader(getURL(Pages.data));
assertThat(header.containsKey("Content-Length"), is(true));
assertThat(header.get("Content-Length").get(0), is(Integer.toString(dataLength)));
}
@Test
public void testGetDataRange() throws Exception {
int dataOffset = 10;
testHandler.setTestData(generateRandomData(TEST_DATA_SIZE));
byte[] subSet = Arrays.copyOfRange(testHandler.getTestData(), dataOffset, TEST_DATA_SIZE);
byte[] data = cut.getDataRange(getURL(Pages.data), dataOffset, TEST_DATA_SIZE-dataOffset);
assertThat(data, is(subSet));
}
@Test(timeout = READ_TIMEOUT_TEST, expected = TimeoutException.class)
public void testGetDataRangeTimeout() throws Exception {
int dataOffset = 10;
cut.setTimeout(READ_TIMEOUT_CLASS, TimeUnit.MILLISECONDS);
cut.getDataRange(getURL(Pages.wait), dataOffset, TEST_DATA_SIZE-dataOffset);
}
@Test(timeout = 5000)
public void testReUse() throws Exception {
int iterations = 3;
for (int i=0; i<iterations; i++){
testHandler.setTestData(generateRandomData(TEST_DATA_SIZE));
assertThat(cut.getData(getURL(Pages.data)), is(testHandler.getTestData()));
}
}
@Test(expected=IllegalArgumentException.class)
public void testSetUserAgentNull() throws Exception {
cut.setUserAgent(null);
}
@Test
public void testSetUserAgent() throws Exception {
String newAgent = "Foobar";
cut.setUserAgent(newAgent);
byte[] data = cut.getData(getURL(Pages.agent));
String decoded = new String(data, StandardCharsets.UTF_8);
assertThat(decoded, is(newAgent));
}
@Test
public void testDefaultUserAgent() throws Exception {
assertThat(cut.getUserAgent(), is("Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0"));
}
@Test
public void testGetTimeoutInMilliseconds() throws Exception {
cut.setTimeout(2, TimeUnit.SECONDS);
assertThat(cut.getTimeoutInMilliseconds(), is(2000L));
}
static class TestHandler extends AbstractHandler {
private byte[] testData;
public byte[] getTestData() {
return testData;
}
public void setTestData(byte[] testData) {
this.testData = testData;
}
@Override
public void handle(String arg0, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException,
ServletException {
response.setContentType("application/octet-stream");
response.setStatus(HttpServletResponse.SC_OK);
baseRequest.setHandled(true);
String pageName = extractPage(request);
Pages selectedPage = null;
try {
selectedPage = Pages.valueOf(Pages.class, pageName);
} catch (IllegalArgumentException iae) {
fail(pageName + " is not a valid page Enum");
} catch (NullPointerException npe) {
fail("Page name was null");
}
switch (selectedPage) {
case data:
processDataRequest(request, response, this.testData);
break;
case wait:
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
break;
case notok:
response.setStatus(Response.SC_BAD_REQUEST);
break;
case error:
response.sendError(Response.SC_BAD_REQUEST);
break;
case agent:
processDataRequest(request, response, request.getHeader("user-agent").getBytes(StandardCharsets.UTF_8));
break;
default:
throw new IllegalArgumentException("Unknown page");
}
}
}
private static boolean isRangeRequest(HttpServletRequest request) {
return (request.getHeader("Range") != null && request.getHeader("Range").contains("bytes"));
}
private static void processRangeRequest(HttpServletRequest request, HttpServletResponse response, byte[] data) throws IOException {
StringBuilder sb = new StringBuilder();
sb.append(request.getHeader("Range"));
sb.replace(0, 6, "");
String[] marker = sb.toString().split("-");
int start = Integer.parseInt(marker[0]);
int offset = start + Integer.parseInt(marker[1]);
byte[] selection = Arrays.copyOfRange(data, start, offset);
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
response.getOutputStream().write(selection);
response.getOutputStream().close();
}
private static void processDataRequest(HttpServletRequest request, HttpServletResponse response, byte[] data) throws IOException {
if (isRangeRequest(request)) {
processRangeRequest(request, response, data);
} else {
response.getOutputStream().write(data);
response.getOutputStream().close();
}
}
private byte[] generateRandomData(int numOfBytes) {
byte[] randomData = new byte[numOfBytes];
for (int i = 0; i < numOfBytes; i++) {
randomData[i] = (byte) (Math.random() * Byte.MAX_VALUE);
}
return randomData;
}
private static String extractPage(HttpServletRequest request) {
String requestUri = request.getRequestURI();
int pageIndexStart = requestUri.lastIndexOf("/");
String pageName = requestUri.substring(pageIndexStart + 1);
return pageName;
}
private URL getURL(Pages page) throws MalformedURLException {
if (pageURLs.containsKey(page)) {
return pageURLs.get(page);
} else {
URL pageUrl = createURL(page);
pageURLs.put(page, pageUrl);
return pageUrl;
}
}
private URL createURL(Pages page) throws MalformedURLException {
String pageName = page.toString();
String constructedUrl = "http://localhost:" + SERVER_PORT + "/" + pageName;
URL pageUrl = new URL(constructedUrl);
return pageUrl;
}
}