aboutsummaryrefslogtreecommitdiffstats
path: root/jdisc_http_service/src/test
diff options
context:
space:
mode:
authorBjørn Christian Seime <bjorncs@oath.com>2017-11-08 16:33:27 +0100
committerBjørn Christian Seime <bjorncs@oath.com>2017-11-08 16:33:27 +0100
commitf93282be4b7a50a0ecec38832ad2d7b3bf587ef8 (patch)
treed4e3309e290f376f0c9bd644716a44e39ed7ab03 /jdisc_http_service/src/test
parent782fca3604665eb5b29fefb0f97b8d856c627ff3 (diff)
Move test utils to test source directory
Diffstat (limited to 'jdisc_http_service/src/test')
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/test/ChunkReader.java124
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/test/FilterTestDriver.java70
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/test/RemoteClient.java53
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/test/RemoteServer.java110
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/test/ServerTestDriver.java146
5 files changed, 503 insertions, 0 deletions
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/test/ChunkReader.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/test/ChunkReader.java
new file mode 100644
index 00000000000..a550a013a3b
--- /dev/null
+++ b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/test/ChunkReader.java
@@ -0,0 +1,124 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.jdisc.http.test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class ChunkReader {
+
+ private static final Pattern CONTENT_LENGTH = Pattern.compile(".+^content-length: (\\d+)$.*",
+ Pattern.CASE_INSENSITIVE |
+ Pattern.MULTILINE |
+ Pattern.DOTALL);
+ private static final Pattern CHUNKED_ENCODING = Pattern.compile(".+^transfer-encoding: chunked$.*",
+ Pattern.CASE_INSENSITIVE |
+ Pattern.MULTILINE |
+ Pattern.DOTALL);
+ private final InputStream in;
+ private StringBuilder reading = new StringBuilder();
+ private boolean readingHeader = true;
+
+ public ChunkReader(InputStream in) {
+ this.in = in;
+ }
+
+ public boolean isEndOfContent() throws IOException {
+ if (in.available() != 0) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(in.available()).append(": ");
+ for(int c = in.read(); c != -1; c = in.read()) {
+ sb.append('\'');
+ sb.append(c);
+ sb.append("' ");
+ }
+ throw new IllegalStateException("This is not the end '" + sb.toString());
+ }
+ return in.available() == 0;
+ }
+
+ public String readChunk() throws IOException {
+ while (true) {
+ String ret = removeNextChunk();
+ if (ret != null) {
+ return ret;
+ }
+ readFromStream();
+ }
+ }
+
+ private String readContent(int length) throws IOException {
+ while (reading.length() < length) {
+ readFromStream();
+ }
+ return splitReadBuffer(length);
+ }
+
+ private void readFromStream() throws IOException {
+ byte[] buf = new byte[4096];
+ try {
+ while (!Thread.currentThread().isInterrupted()) {
+ int len = in.read(buf, 0, buf.length);
+ if (len < 0) {
+ throw new IOException("Socket is closed.");
+ }
+ if (len > 0) {
+ reading.append(StandardCharsets.UTF_8.decode(ByteBuffer.wrap(buf, 0, len)));
+ break;
+ }
+ Thread.sleep(10);
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ private String removeNextChunk() throws IOException {
+ if (readingHeader) {
+ int pos = reading.indexOf("\r\n\r\n");
+ if (pos < 0) {
+ return null;
+ }
+ String ret = splitReadBuffer(pos + 4);
+ Matcher m = CONTENT_LENGTH.matcher(ret);
+ if (m.matches()) {
+ ret += readContent(Integer.valueOf(m.group(1)));
+ }
+ readingHeader = !CHUNKED_ENCODING.matcher(ret).matches();
+ return ret;
+ } else if (reading.indexOf("0\r\n") == 0) {
+ int pos = reading.indexOf("\r\n\r\n", 1);
+ if (pos < 0) {
+ return null;
+ }
+ readingHeader = true;
+ return splitReadBuffer(pos + 4);
+ } else {
+ int pos = reading.indexOf("\r\n");
+ if (pos < 0) {
+ return null;
+ }
+ pos = reading.indexOf("\r\n", pos + 2);
+ if (pos < 0) {
+ return null;
+ }
+ return splitReadBuffer(pos + 2);
+ }
+ }
+
+ private String splitReadBuffer(int pos) {
+ String ret = reading.substring(0, pos);
+ if (pos < reading.length()) {
+ reading = new StringBuilder(reading.substring(pos));
+ } else {
+ reading = new StringBuilder();
+ }
+ return ret;
+ }
+}
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/test/FilterTestDriver.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/test/FilterTestDriver.java
new file mode 100644
index 00000000000..1532bc65bdf
--- /dev/null
+++ b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/test/FilterTestDriver.java
@@ -0,0 +1,70 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.jdisc.http.test;
+
+import com.yahoo.jdisc.Request;
+import com.yahoo.jdisc.Response;
+import com.yahoo.jdisc.application.BindingRepository;
+import com.yahoo.jdisc.handler.AbstractRequestHandler;
+import com.yahoo.jdisc.handler.ContentChannel;
+import com.yahoo.jdisc.handler.ResponseDispatch;
+import com.yahoo.jdisc.handler.ResponseHandler;
+import com.yahoo.jdisc.http.HttpRequest;
+import com.yahoo.jdisc.http.filter.RequestFilter;
+import com.yahoo.jdisc.http.filter.ResponseFilter;
+
+import java.io.IOException;
+import java.util.concurrent.Exchanger;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import static com.yahoo.jdisc.http.test.ServerTestDriver.newFilterModule;
+
+/**
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ *
+ * TODO: dead code?
+ */
+public class FilterTestDriver {
+
+ private final ServerTestDriver driver;
+ private final MyRequestHandler requestHandler;
+
+ private FilterTestDriver(ServerTestDriver driver, MyRequestHandler requestHandler) {
+ this.driver = driver;
+ this.requestHandler = requestHandler;
+ }
+
+ public boolean close() throws IOException {
+ return driver.close();
+ }
+
+ public HttpRequest filterRequest(String request) throws IOException, TimeoutException, InterruptedException {
+ driver.client().writeRequest(request);
+ return (HttpRequest)requestHandler.exchanger.exchange(null, 60, TimeUnit.SECONDS);
+ }
+
+ public static FilterTestDriver newInstance(final BindingRepository<RequestFilter> requestFilters,
+ final BindingRepository<ResponseFilter> responseFilters)
+ throws IOException {
+ MyRequestHandler handler = new MyRequestHandler();
+ return new FilterTestDriver(ServerTestDriver.newInstance(handler,
+ newFilterModule(requestFilters, responseFilters)),
+ handler);
+ }
+
+ private static class MyRequestHandler extends AbstractRequestHandler {
+
+ final Exchanger<Request> exchanger = new Exchanger<>();
+
+ @Override
+ public ContentChannel handleRequest(Request request, ResponseHandler handler) {
+ ResponseDispatch.newInstance(Response.Status.OK).dispatch(handler);
+ try {
+ exchanger.exchange(request);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+ }
+}
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/test/RemoteClient.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/test/RemoteClient.java
new file mode 100644
index 00000000000..dd6033c9975
--- /dev/null
+++ b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/test/RemoteClient.java
@@ -0,0 +1,53 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.jdisc.http.test;
+
+import com.yahoo.jdisc.http.server.jetty.JettyHttpServer;
+import com.yahoo.jdisc.http.ssl.SslContextFactory;
+import com.yahoo.jdisc.http.ssl.SslKeyStore;
+
+import javax.net.ssl.SSLContext;
+import java.io.IOException;
+import java.net.Socket;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ */
+public class RemoteClient extends ChunkReader {
+
+ private final Socket socket;
+
+ private RemoteClient(Socket socket) throws IOException {
+ super(socket.getInputStream());
+ this.socket = socket;
+ }
+
+ public void close() throws IOException {
+ socket.close();
+ }
+
+ public void writeRequest(String request) throws IOException {
+ socket.getOutputStream().write(request.getBytes(StandardCharsets.UTF_8));
+ }
+
+ public static RemoteClient newInstance(JettyHttpServer server) throws IOException {
+ return newInstance(server.getListenPort());
+ }
+
+ public static RemoteClient newInstance(int listenPort) throws IOException {
+ return new RemoteClient(new Socket("localhost", listenPort));
+ }
+
+ public static RemoteClient newSslInstance(int listenPort, SslKeyStore sslKeyStore) throws IOException {
+ SSLContext ctx = SslContextFactory.newInstanceFromTrustStore(sslKeyStore).getServerSSLContext();
+ if (ctx == null) {
+ throw new RuntimeException("Failed to create socket with SSLContext.");
+ }
+ return new RemoteClient(ctx.getSocketFactory().createSocket("localhost", listenPort));
+ }
+
+ public static RemoteClient newSslInstance(JettyHttpServer server, SslKeyStore keyStore) throws IOException {
+ return newSslInstance(server.getListenPort(), keyStore);
+ }
+
+}
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/test/RemoteServer.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/test/RemoteServer.java
new file mode 100644
index 00000000000..62b4bb306ed
--- /dev/null
+++ b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/test/RemoteServer.java
@@ -0,0 +1,110 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.jdisc.http.test;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen</a>
+ */
+public class RemoteServer implements Runnable {
+
+ private final Thread thread = new Thread(this, "RemoteServer@" + System.identityHashCode(this));
+ private final LinkedBlockingQueue<Socket> clients = new LinkedBlockingQueue<>();
+ private final ServerSocket server;
+
+ private RemoteServer(int listenPort) throws IOException {
+ this.server = new ServerSocket(listenPort);
+ }
+
+ @Override
+ public void run() {
+ try {
+ while (!Thread.interrupted()) {
+ Socket client = server.accept();
+ if (client != null) {
+ clients.add(client);
+ }
+ }
+ } catch (IOException e) {
+ if (!server.isClosed()) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public URI newRequestUri(String uri) {
+ return newRequestUri(URI.create(uri));
+ }
+
+ public URI newRequestUri(URI uri) {
+ URI serverUri = connectionSpec();
+ try {
+ return new URI(serverUri.getScheme(), serverUri.getUserInfo(), serverUri.getHost(),
+ serverUri.getPort(), uri.getPath(), uri.getQuery(), uri.getFragment());
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ public URI connectionSpec() {
+ return URI.create("http://localhost:" + server.getLocalPort() + "/");
+ }
+
+ public Connection awaitConnection(int timeout, TimeUnit unit) throws InterruptedException, IOException {
+ Socket client = clients.poll(timeout, unit);
+ if (client == null) {
+ return null;
+ }
+ return new Connection(client);
+ }
+
+ public boolean close(int timeout, TimeUnit unit) {
+ try {
+ server.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ }
+ try {
+ thread.join(unit.toMillis(timeout));
+ } catch (InterruptedException e) {
+ return false;
+ }
+ return !thread.isAlive();
+ }
+
+ public static RemoteServer newInstance() throws IOException {
+ RemoteServer ret = new RemoteServer(0);
+ ret.thread.start();
+ return ret;
+ }
+
+ public static class Connection extends ChunkReader {
+
+ private final Socket socket;
+ private final PrintWriter out;
+
+ private Connection(Socket socket) throws IOException {
+ super(socket.getInputStream());
+ this.socket = socket;
+ this.out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
+ }
+
+ public void writeChunk(String chunk) {
+ out.print(chunk);
+ }
+
+ public void close() throws IOException {
+ out.close();
+ socket.close();
+ }
+ }
+}
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/test/ServerTestDriver.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/test/ServerTestDriver.java
new file mode 100644
index 00000000000..03e2257ce70
--- /dev/null
+++ b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/test/ServerTestDriver.java
@@ -0,0 +1,146 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.jdisc.http.test;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Module;
+import com.google.inject.TypeLiteral;
+import com.yahoo.jdisc.application.BindingRepository;
+import com.yahoo.jdisc.application.ContainerActivator;
+import com.yahoo.jdisc.application.ContainerBuilder;
+import com.yahoo.jdisc.handler.RequestHandler;
+import com.yahoo.jdisc.http.HttpRequest;
+import com.yahoo.jdisc.http.filter.RequestFilter;
+import com.yahoo.jdisc.http.filter.ResponseFilter;
+import com.yahoo.jdisc.http.server.jetty.JettyHttpServer;
+import com.yahoo.jdisc.http.ssl.SslKeyStore;
+import com.yahoo.jdisc.test.TestDriver;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a>
+ */
+public class ServerTestDriver {
+
+ private final TestDriver driver;
+ private final JettyHttpServer server;
+ private final RemoteClient client;
+
+ private ServerTestDriver(TestDriver driver, JettyHttpServer server, RemoteClient client) {
+ this.driver = driver;
+ this.server = server;
+ this.client = client;
+ }
+
+ public boolean close() throws IOException {
+ client.close();
+ server.close();
+ server.release();
+ return driver.close();
+ }
+
+ public TestDriver parent() {
+ return driver;
+ }
+
+ public ContainerActivator containerActivator() {
+ return driver;
+ }
+
+ public JettyHttpServer server() {
+ return server;
+ }
+
+ public RemoteClient client() {
+ return client;
+ }
+
+ public HttpRequest newRequest(HttpRequest.Method method, String uri, HttpRequest.Version version) {
+ return HttpRequest.newServerRequest(driver, newRequestUri(uri), method, version);
+ }
+
+ public URI newRequestUri(String uri) {
+ return newRequestUri(URI.create(uri));
+ }
+
+ public URI newRequestUri(URI uri) {
+ try {
+ return new URI("http", null, "locahost",
+ server.getListenPort(), uri.getPath(), uri.getQuery(), uri.getFragment());
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ public static ServerTestDriver newInstance(RequestHandler requestHandler, Module... guiceModules) throws IOException {
+ return newInstance(requestHandler, Arrays.asList(guiceModules));
+ }
+
+ public static ServerTestDriver newInstance(RequestHandler requestHandler, Iterable<Module> guiceModules)
+ throws IOException {
+ List<Module> lst = new LinkedList<>();
+ lst.add(newDefaultModule());
+ for (Module module : guiceModules) {
+ lst.add(module);
+ }
+ TestDriver driver = TestDriver.newSimpleApplicationInstanceWithoutOsgi(lst.toArray(new Module[lst.size()]));
+ ContainerBuilder builder = driver.newContainerBuilder();
+ builder.serverBindings().bind("*://*/*", requestHandler);
+ JettyHttpServer server = builder.guiceModules().getInstance(JettyHttpServer.class);
+ return newInstance(null, driver, builder, server);
+ }
+
+ private static ServerTestDriver newInstance(SslKeyStore clientTrustStore, TestDriver driver, ContainerBuilder builder,
+ JettyHttpServer server) throws IOException {
+ builder.serverProviders().install(server);
+ driver.activateContainer(builder);
+ try {
+ server.start();
+ } catch (RuntimeException e) {
+ server.release();
+ driver.close();
+ throw e;
+ }
+ RemoteClient client;
+ if (clientTrustStore == null) {
+ client = RemoteClient.newInstance(server);
+ } else {
+ client = RemoteClient.newSslInstance(server, clientTrustStore);
+ }
+ return new ServerTestDriver(driver, server, client);
+ }
+
+ public static Module newDefaultModule() {
+ return new AbstractModule() {
+
+ @Override
+ protected void configure() {
+ bind(new TypeLiteral<BindingRepository<RequestFilter>>() { })
+ .toInstance(new BindingRepository<>());
+ bind(new TypeLiteral<BindingRepository<ResponseFilter>>() { })
+ .toInstance(new BindingRepository<>());
+ }
+ };
+ }
+
+ public static Module newFilterModule(final BindingRepository<RequestFilter> requestFilters,
+ final BindingRepository<ResponseFilter> responseFilters) {
+ return new AbstractModule() {
+
+ @Override
+ protected void configure() {
+ if (requestFilters != null) {
+ bind(new TypeLiteral<BindingRepository<RequestFilter>>() { }).toInstance(requestFilters);
+ }
+ if (responseFilters != null) {
+ bind(new TypeLiteral<BindingRepository<ResponseFilter>>() { }).toInstance(responseFilters);
+ }
+ }
+ };
+ }
+}