summaryrefslogtreecommitdiffstats
path: root/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty
diff options
context:
space:
mode:
authorgjoranv <gv@verizonmedia.com>2021-03-23 21:21:39 +0100
committergjoranv <gv@verizonmedia.com>2021-03-23 23:13:01 +0100
commit5617a82f7a32ebcc37be226b27f6ff284f5c896d (patch)
tree45e8b3be33c372971c2b1349d245e223a1f46085 /jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty
parent266046b2bb8bebbb683499f558a05aaf8a4f1ff3 (diff)
Remove the jdisc_http_service module.
- It has been merged into container-core.
Diffstat (limited to 'jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty')
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/AccessLogRequestLogTest.java156
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/BlockingQueueRequestLog.java24
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/ConnectionThrottlerTest.java78
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/ConnectorFactoryTest.java83
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/ErrorResponseContentCreatorTest.java44
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/ExceptionWrapperTest.java51
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/FilterTestCase.java667
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpRequestFactoryTest.java204
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpResponseStatisticsCollectorTest.java221
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerConformanceTest.java847
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerTest.java1201
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/InMemoryConnectionLog.java25
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/InMemoryRequestLog.java20
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/JDiscHttpServletTest.java80
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/MetricConsumerMock.java28
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/SimpleHttpClient.java202
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/SslHandshakeFailedListenerTest.java42
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/TestDriver.java79
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/TestDrivers.java94
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/servlet/JDiscFilterForServletTest.java166
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/servlet/ServletAccessLoggingTest.java64
-rw-r--r--jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/servlet/ServletTestBase.java132
22 files changed, 0 insertions, 4508 deletions
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/AccessLogRequestLogTest.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/AccessLogRequestLogTest.java
deleted file mode 100644
index 6370912af48..00000000000
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/AccessLogRequestLogTest.java
+++ /dev/null
@@ -1,156 +0,0 @@
-// 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.server.jetty;
-
-import com.yahoo.container.logging.AccessLogEntry;
-import com.yahoo.container.logging.RequestLog;
-import com.yahoo.container.logging.RequestLogEntry;
-import com.yahoo.jdisc.http.ConnectorConfig;
-import com.yahoo.jdisc.http.ServerConfig;
-import org.eclipse.jetty.http.MetaData;
-import org.eclipse.jetty.server.HttpChannel;
-import org.eclipse.jetty.server.HttpConnection;
-import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.Response;
-import org.junit.Test;
-
-import java.util.List;
-import java.util.Optional;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.CoreMatchers.nullValue;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-/**
- * @author Oyvind Bakksjo
- * @author bjorncs
- */
-public class AccessLogRequestLogTest {
- @Test
- public void requireThatQueryWithUnquotedSpecialCharactersIsHandled() {
- final Request jettyRequest = createRequestMock();
- when(jettyRequest.getRequestURI()).thenReturn("/search/");
- when(jettyRequest.getQueryString()).thenReturn("query=year:>2010");
-
- InMemoryRequestLog requestLog = new InMemoryRequestLog();
- doAccessLoggingOfRequest(requestLog, jettyRequest);
- RequestLogEntry entry = requestLog.entries().get(0);
-
- assertThat(entry.rawPath().get(), is(not(nullValue())));
- assertTrue(entry.rawQuery().isPresent());
- }
-
- @Test
- public void requireThatDoubleQuotingIsNotPerformed() {
- final Request jettyRequest = createRequestMock();
- final String path = "/search/";
- when(jettyRequest.getRequestURI()).thenReturn(path);
- final String query = "query=year%252010+%3B&customParameter=something";
- when(jettyRequest.getQueryString()).thenReturn(query);
-
- InMemoryRequestLog requestLog = new InMemoryRequestLog();
- doAccessLoggingOfRequest(requestLog, jettyRequest);
- RequestLogEntry entry = requestLog.entries().get(0);
-
- assertThat(entry.rawPath().get(), is(path));
- assertThat(entry.rawQuery().get(), is(query));
-
- }
-
- @Test
- public void raw_path_and_query_are_set_from_request() {
- Request jettyRequest = createRequestMock();
- String rawPath = "//search/";
- when(jettyRequest.getRequestURI()).thenReturn(rawPath);
- String rawQuery = "q=%%2";
- when(jettyRequest.getQueryString()).thenReturn(rawQuery);
-
- InMemoryRequestLog requestLog = new InMemoryRequestLog();
- doAccessLoggingOfRequest(requestLog, jettyRequest);
- RequestLogEntry entry = requestLog.entries().get(0);
- assertThat(entry.rawPath().get(), is(rawPath));
- Optional<String> actualRawQuery = entry.rawQuery();
- assertThat(actualRawQuery.isPresent(), is(true));
- assertThat(actualRawQuery.get(), is(rawQuery));
- }
-
- @Test
- public void verify_x_forwarded_for_precedence () {
- Request jettyRequest = createRequestMock();
- when(jettyRequest.getRequestURI()).thenReturn("//search/");
- when(jettyRequest.getQueryString()).thenReturn("q=%%2");
- when(jettyRequest.getHeader("x-forwarded-for")).thenReturn("1.2.3.4");
- when(jettyRequest.getHeader("y-ra")).thenReturn("2.3.4.5");
-
- InMemoryRequestLog requestLog = new InMemoryRequestLog();
- doAccessLoggingOfRequest(requestLog, jettyRequest);
- RequestLogEntry entry = requestLog.entries().get(0);
- assertThat(entry.remoteAddress().get(), is("1.2.3.4"));
- }
-
- @Test
- public void verify_x_forwarded_port_precedence () {
- Request jettyRequest = createRequestMock();
- when(jettyRequest.getRequestURI()).thenReturn("//search/");
- when(jettyRequest.getQueryString()).thenReturn("q=%%2");
- when(jettyRequest.getHeader("X-Forwarded-Port")).thenReturn("80");
- when(jettyRequest.getHeader("y-rp")).thenReturn("8080");
-
- InMemoryRequestLog requestLog = new InMemoryRequestLog();
- doAccessLoggingOfRequest(requestLog, jettyRequest);
- RequestLogEntry entry = requestLog.entries().get(0);
- assertThat(entry.remotePort().getAsInt(), is(80));
- }
-
- @Test
- public void defaults_to_peer_port_if_remote_port_header_is_invalid() {
- final Request jettyRequest = createRequestMock();
- when(jettyRequest.getRequestURI()).thenReturn("/search/");
- when(jettyRequest.getHeader("X-Forwarded-Port")).thenReturn("8o8o");
- when(jettyRequest.getRemotePort()).thenReturn(80);
-
- InMemoryRequestLog requestLog = new InMemoryRequestLog();
- doAccessLoggingOfRequest(requestLog, jettyRequest);
- RequestLogEntry entry = requestLog.entries().get(0);
- assertFalse(entry.remotePort().isPresent());
- assertThat(entry.peerPort().getAsInt(), is(80));
- }
-
- private void doAccessLoggingOfRequest(RequestLog requestLog, Request jettyRequest) {
- ServerConfig.AccessLog config = new ServerConfig.AccessLog(
- new ServerConfig.AccessLog.Builder()
- .remoteAddressHeaders(List.of("x-forwarded-for", "y-ra"))
- .remotePortHeaders(List.of("X-Forwarded-Port", "y-rp")));
- new AccessLogRequestLog(requestLog, config).log(jettyRequest, createResponseMock());
- }
-
- private static Request createRequestMock() {
- JDiscServerConnector serverConnector = mock(JDiscServerConnector.class);
- int localPort = 1234;
- when(serverConnector.connectorConfig()).thenReturn(new ConnectorConfig(new ConnectorConfig.Builder().listenPort(localPort)));
- when(serverConnector.getLocalPort()).thenReturn(localPort);
- HttpConnection httpConnection = mock(HttpConnection.class);
- when(httpConnection.getConnector()).thenReturn(serverConnector);
- Request request = mock(Request.class);
- when(request.getMethod()).thenReturn("GET");
- when(request.getRemoteAddr()).thenReturn("localhost");
- when(request.getRemotePort()).thenReturn(12345);
- when(request.getProtocol()).thenReturn("HTTP/1.1");
- when(request.getScheme()).thenReturn("http");
- when(request.getTimeStamp()).thenReturn(0L);
- when(request.getAttribute(JDiscHttpServlet.ATTRIBUTE_NAME_ACCESS_LOG_ENTRY)).thenReturn(new AccessLogEntry());
- when(request.getAttribute("org.eclipse.jetty.server.HttpConnection")).thenReturn(httpConnection);
- return request;
- }
-
- private Response createResponseMock() {
- Response response = mock(Response.class);
- when(response.getHttpChannel()).thenReturn(mock(HttpChannel.class));
- when(response.getCommittedMetaData()).thenReturn(mock(MetaData.Response.class));
- return response;
- }
-}
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/BlockingQueueRequestLog.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/BlockingQueueRequestLog.java
deleted file mode 100644
index c1a2bea8ac4..00000000000
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/BlockingQueueRequestLog.java
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.jdisc.http.server.jetty;
-
-import com.yahoo.container.logging.RequestLog;
-import com.yahoo.container.logging.RequestLogEntry;
-
-import java.time.Duration;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingDeque;
-import java.util.concurrent.TimeUnit;
-
-/**
- * @author bjorncs
- */
-class BlockingQueueRequestLog implements RequestLog {
-
- private final BlockingQueue<RequestLogEntry> entries = new LinkedBlockingDeque<>();
-
- @Override public void log(RequestLogEntry entry) { entries.offer(entry); }
-
- RequestLogEntry poll(Duration timeout) throws InterruptedException {
- return entries.poll(timeout.toMillis(), TimeUnit.MILLISECONDS);
- }
-}
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/ConnectionThrottlerTest.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/ConnectionThrottlerTest.java
deleted file mode 100644
index 65eb7e1c145..00000000000
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/ConnectionThrottlerTest.java
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2019 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.jdisc.http.server.jetty;
-
-import com.yahoo.jdisc.http.ConnectorConfig;
-import org.eclipse.jetty.server.AbstractConnector;
-import org.eclipse.jetty.util.component.AbstractLifeCycle;
-import org.eclipse.jetty.util.statistic.RateStatistic;
-import org.eclipse.jetty.util.thread.Scheduler;
-import org.junit.Test;
-
-import java.util.concurrent.TimeUnit;
-
-import static org.junit.Assert.assertNotNull;
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.internal.verification.VerificationModeFactory.times;
-
-/**
- * @author bjorncs
- */
-public class ConnectionThrottlerTest {
-
- @Test
- public void throttles_when_any_resource_check_exceeds_configured_threshold() {
- Runtime runtime = mock(Runtime.class);
- when(runtime.maxMemory()).thenReturn(100l);
- RateStatistic rateStatistic = new RateStatistic(1, TimeUnit.HOURS);
- MockScheduler scheduler = new MockScheduler();
- ConnectorConfig.Throttling config = new ConnectorConfig.Throttling(new ConnectorConfig.Throttling.Builder()
- .maxHeapUtilization(0.8)
- .maxAcceptRate(1));
-
- AbstractConnector connector = mock(AbstractConnector.class);
-
- ConnectionThrottler throttler = new ConnectionThrottler(runtime, rateStatistic, scheduler, connector, config);
-
- // Heap utilization above configured threshold, but connection rate below threshold.
- when(runtime.freeMemory()).thenReturn(10l);
- when(connector.isAccepting()).thenReturn(true);
- throttler.onAccepting(null);
- assertNotNull(scheduler.task);
- verify(connector).setAccepting(false);
-
- // Heap utilization below threshold, but connection rate above threshold.
- when(runtime.freeMemory()).thenReturn(80l);
- rateStatistic.record();
- rateStatistic.record(); // above accept rate limit (2 > 1)
- scheduler.task.run(); // run unthrottleIfBelowThresholds()
- verify(connector, times(1)).setAccepting(anyBoolean()); // verify setAccepting has not been called any mores times
-
- // Both heap utilization and accept rate below threshold
- when(runtime.freeMemory()).thenReturn(80l);
- when(connector.isAccepting()).thenReturn(false);
- rateStatistic.reset();
- scheduler.task.run(); // run unthrottleIfBelowThresholds()
- verify(connector).setAccepting(true);
-
- // Both heap utilization and accept rate below threshold
- when(connector.isAccepting()).thenReturn(true);
- when(runtime.freeMemory()).thenReturn(80l);
- rateStatistic.record();
- throttler.onAccepting(null);
- verify(connector, times(2)).setAccepting(anyBoolean()); // verify setAccepting has not been called any mores times
- }
-
- private static class MockScheduler extends AbstractLifeCycle implements Scheduler {
- Runnable task;
-
- @Override
- public Task schedule(Runnable task, long delay, TimeUnit units) {
- this.task = task;
- return () -> false;
- }
- }
-
-} \ No newline at end of file
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/ConnectorFactoryTest.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/ConnectorFactoryTest.java
deleted file mode 100644
index df794c7ecb8..00000000000
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/ConnectorFactoryTest.java
+++ /dev/null
@@ -1,83 +0,0 @@
-// 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.server.jetty;
-
-import com.yahoo.jdisc.Metric;
-import com.yahoo.jdisc.http.ConnectorConfig;
-import com.yahoo.jdisc.http.ServerConfig;
-import com.yahoo.jdisc.http.ssl.impl.ConfiguredSslContextFactoryProvider;
-import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.handler.AbstractHandler;
-import org.junit.Test;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.util.Map;
-
-import static org.hamcrest.CoreMatchers.equalTo;
-
-/**
- * @author Einar M R Rosenvinge
- */
-public class ConnectorFactoryTest {
-
- @Test
- public void requireThatServerCanBindChannel() throws Exception {
- Server server = new Server();
- try {
- ConnectorConfig config = new ConnectorConfig(new ConnectorConfig.Builder());
- ConnectorFactory factory = createConnectorFactory(config);
- JettyConnectionLogger connectionLogger = new JettyConnectionLogger(
- new ServerConfig.ConnectionLog.Builder().enabled(false).build(),
- new VoidConnectionLog());
- JDiscServerConnector connector =
- (JDiscServerConnector)factory.createConnector(new DummyMetric(), server, connectionLogger);
- server.addConnector(connector);
- server.setHandler(new HelloWorldHandler());
- server.start();
-
- SimpleHttpClient client = new SimpleHttpClient(null, connector.getLocalPort(), false);
- SimpleHttpClient.RequestExecutor ex = client.newGet("/blaasdfnb");
- SimpleHttpClient.ResponseValidator val = ex.execute();
- val.expectContent(equalTo("Hello world"));
- } finally {
- try {
- server.stop();
- } catch (Exception e) {
- //ignore
- }
- }
- }
-
- private static ConnectorFactory createConnectorFactory(ConnectorConfig config) {
- return new ConnectorFactory(config, new ConfiguredSslContextFactoryProvider(config));
- }
-
- private static class HelloWorldHandler extends AbstractHandler {
- @Override
- public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException {
- response.getWriter().write("Hello world");
- response.getWriter().flush();
- response.getWriter().close();
- baseRequest.setHandled(true);
- }
- }
-
- private static class DummyMetric implements Metric {
- @Override
- public void set(String key, Number val, Context ctx) { }
-
- @Override
- public void add(String key, Number val, Context ctx) { }
-
- @Override
- public Context createContext(Map<String, ?> properties) {
- return new DummyContext();
- }
- }
-
- private static class DummyContext implements Metric.Context {
- }
-
-}
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/ErrorResponseContentCreatorTest.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/ErrorResponseContentCreatorTest.java
deleted file mode 100644
index d66f22801f7..00000000000
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/ErrorResponseContentCreatorTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-// 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.server.jetty;
-
-
-import org.junit.Test;
-
-import javax.servlet.http.HttpServletResponse;
-import java.nio.charset.StandardCharsets;
-import java.util.Optional;
-
-import static org.junit.Assert.assertEquals;
-
-
-/**
- * @author bjorncs
- */
-public class ErrorResponseContentCreatorTest {
-
- @Test
- public void response_content_matches_expected_string() {
- String expectedHtml =
- "<html>\n" +
- "<head>\n" +
- "<meta http-equiv=\"Content-Type\" content=\"text/html;charset=ISO-8859-1\"/>\n" +
- "<title>Error 200</title>\n" +
- "</head>\n" +
- "<body>\n" +
- "<h2>HTTP ERROR: 200</h2>\n" +
- "<p>Problem accessing http://foo.bar. Reason:\n" +
- "<pre> My custom error message</pre></p>\n" +
- "<hr/>\n" +
- "</body>\n" +
- "</html>\n";
-
- ErrorResponseContentCreator c = new ErrorResponseContentCreator();
- byte[] rawContent = c.createErrorContent(
- "http://foo.bar",
- HttpServletResponse.SC_OK,
- Optional.of("My custom error message"));
- String actualHtml = new String(rawContent, StandardCharsets.ISO_8859_1);
- assertEquals(expectedHtml, actualHtml);
- }
-
-}
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/ExceptionWrapperTest.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/ExceptionWrapperTest.java
deleted file mode 100644
index de8df283afe..00000000000
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/ExceptionWrapperTest.java
+++ /dev/null
@@ -1,51 +0,0 @@
-// 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.server.jetty;
-
-import org.junit.Test;
-
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-/**
- * Check basic error message formatting. Do note these tests are sensitive to
- * the line numbering in this file. (And that's a feature, not a bug.)
- *
- * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
- */
-public class ExceptionWrapperTest {
-
- @Test
- public final void requireNoMessageIsOK() {
- final Throwable t = new Throwable();
- final ExceptionWrapper e = new ExceptionWrapper(t);
- final String expected = "Throwable() at com.yahoo.jdisc.http.server.jetty.ExceptionWrapperTest(ExceptionWrapperTest.java:19)";
-
- assertThat(e.getMessage(), equalTo(expected));
- }
-
- @Test
- public final void requireAllWrappedLevelsShowUp() {
- final Throwable t0 = new Throwable("t0");
- final Throwable t1 = new Throwable("t1", t0);
- final Throwable t2 = new Throwable("t2", t1);
- final ExceptionWrapper e = new ExceptionWrapper(t2);
- final String expected = "Throwable(\"t2\") at com.yahoo.jdisc.http.server.jetty.ExceptionWrapperTest(ExceptionWrapperTest.java:30):"
- + " Throwable(\"t1\") at com.yahoo.jdisc.http.server.jetty.ExceptionWrapperTest(ExceptionWrapperTest.java:29):"
- + " Throwable(\"t0\") at com.yahoo.jdisc.http.server.jetty.ExceptionWrapperTest(ExceptionWrapperTest.java:28)";
-
- assertThat(e.getMessage(), equalTo(expected));
- }
-
- @Test
- public final void requireMixOfMessageAndNoMessageWorks() {
- final Throwable t0 = new Throwable("t0");
- final Throwable t1 = new Throwable(t0);
- final Throwable t2 = new Throwable("t2", t1);
- final ExceptionWrapper e = new ExceptionWrapper(t2);
- final String expected = "Throwable(\"t2\") at com.yahoo.jdisc.http.server.jetty.ExceptionWrapperTest(ExceptionWrapperTest.java:43):"
- + " Throwable(\"java.lang.Throwable: t0\") at com.yahoo.jdisc.http.server.jetty.ExceptionWrapperTest(ExceptionWrapperTest.java:42):"
- + " Throwable(\"t0\") at com.yahoo.jdisc.http.server.jetty.ExceptionWrapperTest(ExceptionWrapperTest.java:41)";
-
- assertThat(e.getMessage(), equalTo(expected));
- }
-}
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/FilterTestCase.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/FilterTestCase.java
deleted file mode 100644
index a67656dd5ca..00000000000
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/FilterTestCase.java
+++ /dev/null
@@ -1,667 +0,0 @@
-// 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.server.jetty;
-
-import com.google.inject.AbstractModule;
-import com.google.inject.util.Modules;
-import com.yahoo.container.logging.ConnectionLog;
-import com.yahoo.container.logging.RequestLog;
-import com.yahoo.jdisc.AbstractResource;
-import com.yahoo.jdisc.Request;
-import com.yahoo.jdisc.ResourceReference;
-import com.yahoo.jdisc.Response;
-import com.yahoo.jdisc.handler.AbstractRequestHandler;
-import com.yahoo.jdisc.handler.CompletionHandler;
-import com.yahoo.jdisc.handler.ContentChannel;
-import com.yahoo.jdisc.handler.ResponseDispatch;
-import com.yahoo.jdisc.handler.ResponseHandler;
-import com.yahoo.jdisc.http.ConnectorConfig;
-import com.yahoo.jdisc.http.HttpRequest;
-import com.yahoo.jdisc.http.HttpResponse;
-import com.yahoo.jdisc.http.ServerConfig;
-import com.yahoo.jdisc.http.ServletPathsConfig;
-import com.yahoo.jdisc.http.filter.RequestFilter;
-import com.yahoo.jdisc.http.filter.ResponseFilter;
-import com.yahoo.jdisc.http.filter.ResponseHeaderFilter;
-import com.yahoo.jdisc.http.filter.chain.RequestFilterChain;
-import com.yahoo.jdisc.http.filter.chain.ResponseFilterChain;
-import com.yahoo.jdisc.http.guiceModules.ConnectorFactoryRegistryModule;
-import org.junit.Test;
-import org.mockito.ArgumentCaptor;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-/**
- * @author Oyvind Bakksjo
- * @author bjorncs
- */
-public class FilterTestCase {
- @Test
- public void requireThatRequestFilterIsNotRunOnUnboundPath() throws Exception {
- RequestFilterMockBase filter = mock(RequestFilterMockBase.class);
- FilterBindings filterBindings = new FilterBindings.Builder()
- .addRequestFilter("my-request-filter", filter)
- .addRequestFilterBinding("my-request-filter", "http://*/filtered/*")
- .build();
- final MyRequestHandler requestHandler = new MyRequestHandler();
- final TestDriver testDriver = newDriver(requestHandler, filterBindings);
-
- testDriver.client().get("/status.html");
-
- assertThat(requestHandler.awaitInvocation(), is(true));
- verify(filter, never()).filter(any(HttpRequest.class), any(ResponseHandler.class));
-
- assertThat(testDriver.close(), is(true));
- }
-
- @Test
- public void requireThatRequestFilterIsRunOnBoundPath() throws Exception {
- final RequestFilter filter = mock(RequestFilterMockBase.class);
- FilterBindings filterBindings = new FilterBindings.Builder()
- .addRequestFilter("my-request-filter", filter)
- .addRequestFilterBinding("my-request-filter", "http://*/filtered/*")
- .build();
- final MyRequestHandler requestHandler = new MyRequestHandler();
- final TestDriver testDriver = newDriver(requestHandler, filterBindings);
-
- testDriver.client().get("/filtered/status.html");
-
- assertThat(requestHandler.awaitInvocation(), is(true));
- verify(filter, times(1)).filter(any(HttpRequest.class), any(ResponseHandler.class));
-
- assertThat(testDriver.close(), is(true));
- }
-
- @Test
- public void requireThatRequestFilterChangesAreSeenByRequestHandler() throws Exception {
- final RequestFilter filter = new HeaderRequestFilter("foo", "bar");
- FilterBindings filterBindings = new FilterBindings.Builder()
- .addRequestFilter("my-request-filter", filter)
- .addRequestFilterBinding("my-request-filter", "http://*/*")
- .build();
- final MyRequestHandler requestHandler = new MyRequestHandler();
- final TestDriver testDriver = newDriver(requestHandler, filterBindings);
-
- testDriver.client().get("status.html");
-
- assertThat(requestHandler.awaitInvocation(), is(true));
- assertThat(requestHandler.getHeaderMap().get("foo").get(0), is("bar"));
-
- assertThat(testDriver.close(), is(true));
- }
-
- @Test
- public void requireThatRequestFilterCanRespond() throws Exception {
- FilterBindings filterBindings = new FilterBindings.Builder()
- .addRequestFilter("my-request-filter", new RespondForbiddenFilter())
- .addRequestFilterBinding("my-request-filter", "http://*/*")
- .build();
- final MyRequestHandler requestHandler = new MyRequestHandler();
- final TestDriver testDriver = newDriver(requestHandler, filterBindings);
-
- testDriver.client().get("/status.html").expectStatusCode(is(Response.Status.FORBIDDEN));
-
- assertThat(requestHandler.hasBeenInvokedYet(), is(false));
-
- assertThat(testDriver.close(), is(true));
- }
-
- @Test
- public void requireThatFilterCanHaveNullCompletionHandler() throws Exception {
- final int responseStatus = Response.Status.OK;
- final String responseMessage = "Excellent";
- FilterBindings filterBindings = new FilterBindings.Builder()
- .addRequestFilter("my-request-filter", new NullCompletionHandlerFilter(responseStatus, responseMessage))
- .addRequestFilterBinding("my-request-filter", "http://*/*")
- .build();
- final MyRequestHandler requestHandler = new MyRequestHandler();
- final TestDriver testDriver = newDriver(requestHandler, filterBindings);
-
- testDriver.client().get("/status.html")
- .expectStatusCode(is(responseStatus))
- .expectContent(is(responseMessage));
-
- assertThat(requestHandler.hasBeenInvokedYet(), is(false));
-
- assertThat(testDriver.close(), is(true));
- }
-
- @Test
- public void requireThatRequestFilterExecutionIsExceptionSafe() throws Exception {
- FilterBindings filterBindings = new FilterBindings.Builder()
- .addRequestFilter("my-request-filter", new ThrowingRequestFilter())
- .addRequestFilterBinding("my-request-filter", "http://*/*")
- .build();
- final MyRequestHandler requestHandler = new MyRequestHandler();
- final TestDriver testDriver = newDriver(requestHandler, filterBindings);
-
- testDriver.client().get("/status.html").expectStatusCode(is(Response.Status.INTERNAL_SERVER_ERROR));
-
- assertThat(requestHandler.hasBeenInvokedYet(), is(false));
-
- assertThat(testDriver.close(), is(true));
- }
-
- @Test
- public void requireThatResponseFilterIsNotRunOnUnboundPath() throws Exception {
- final ResponseFilter filter = mock(ResponseFilterMockBase.class);
- FilterBindings filterBindings = new FilterBindings.Builder()
- .addResponseFilter("my-response-filter", filter)
- .addResponseFilterBinding("my-response-filter", "http://*/filtered/*")
- .build();
- final MyRequestHandler requestHandler = new MyRequestHandler();
- final TestDriver testDriver = newDriver(requestHandler, filterBindings);
-
- testDriver.client().get("/status.html");
-
- assertThat(requestHandler.awaitInvocation(), is(true));
- verify(filter, never()).filter(any(Response.class), any(Request.class));
-
- assertThat(testDriver.close(), is(true));
- }
-
- @Test
- public void requireThatResponseFilterIsRunOnBoundPath() throws Exception {
- final ResponseFilter filter = mock(ResponseFilterMockBase.class);
- FilterBindings filterBindings = new FilterBindings.Builder()
- .addResponseFilter("my-response-filter", filter)
- .addResponseFilterBinding("my-response-filter", "http://*/filtered/*")
- .build();
- final MyRequestHandler requestHandler = new MyRequestHandler();
- final TestDriver testDriver = newDriver(requestHandler, filterBindings);
-
- testDriver.client().get("/filtered/status.html");
-
- assertThat(requestHandler.awaitInvocation(), is(true));
- verify(filter, times(1)).filter(any(Response.class), any(Request.class));
-
- assertThat(testDriver.close(), is(true));
- }
-
- @Test
- public void requireThatResponseFilterChangesAreWrittenToResponse() throws Exception {
- FilterBindings filterBindings = new FilterBindings.Builder()
- .addResponseFilter("my-response-filter", new HeaderResponseFilter("foo", "bar"))
- .addResponseFilterBinding("my-response-filter", "http://*/*")
- .build();
- final MyRequestHandler requestHandler = new MyRequestHandler();
- final TestDriver testDriver = newDriver(requestHandler, filterBindings);
-
- testDriver.client().get("/status.html")
- .expectHeader("foo", is("bar"));
-
- assertThat(requestHandler.awaitInvocation(), is(true));
-
- assertThat(testDriver.close(), is(true));
- }
-
- @Test
- public void requireThatResponseFilterExecutionIsExceptionSafe() throws Exception {
- FilterBindings filterBindings = new FilterBindings.Builder()
- .addResponseFilter("my-response-filter", new ThrowingResponseFilter())
- .addResponseFilterBinding("my-response-filter", "http://*/*")
- .build();
- final MyRequestHandler requestHandler = new MyRequestHandler();
- final TestDriver testDriver = newDriver(requestHandler, filterBindings);
-
- testDriver.client().get("/status.html").expectStatusCode(is(Response.Status.INTERNAL_SERVER_ERROR));
-
- assertThat(requestHandler.awaitInvocation(), is(true));
-
- assertThat(testDriver.close(), is(true));
- }
-
- @Test
- public void requireThatRequestFilterAndResponseFilterCanBindToSamePath() throws Exception {
- final RequestFilter requestFilter = mock(RequestFilterMockBase.class);
- final ResponseFilter responseFilter = mock(ResponseFilterMockBase.class);
- final String uriPattern = "http://*/*";
- FilterBindings filterBindings = new FilterBindings.Builder()
- .addRequestFilter("my-request-filter", requestFilter)
- .addRequestFilterBinding("my-request-filter", uriPattern)
- .addResponseFilter("my-response-filter", responseFilter)
- .addResponseFilterBinding("my-response-filter", uriPattern)
- .build();
- final MyRequestHandler requestHandler = new MyRequestHandler();
- final TestDriver testDriver = newDriver(requestHandler, filterBindings);
-
- testDriver.client().get("/status.html");
-
- assertThat(requestHandler.awaitInvocation(), is(true));
- verify(requestFilter, times(1)).filter(any(HttpRequest.class), any(ResponseHandler.class));
- verify(responseFilter, times(1)).filter(any(Response.class), any(Request.class));
-
- assertThat(testDriver.close(), is(true));
- }
-
- @Test
- public void requireThatResponseFromRequestFilterGoesThroughResponseFilter() throws Exception {
- FilterBindings filterBindings = new FilterBindings.Builder()
- .addRequestFilter("my-request-filter", new RespondForbiddenFilter())
- .addRequestFilterBinding("my-request-filter", "http://*/*")
- .addResponseFilter("my-response-filter", new HeaderResponseFilter("foo", "bar"))
- .addResponseFilterBinding("my-response-filter", "http://*/*")
- .build();
- final MyRequestHandler requestHandler = new MyRequestHandler();
- final TestDriver testDriver = newDriver(requestHandler, filterBindings);
-
- testDriver.client().get("/status.html")
- .expectStatusCode(is(Response.Status.FORBIDDEN))
- .expectHeader("foo", is("bar"));
-
- assertThat(requestHandler.hasBeenInvokedYet(), is(false));
-
- assertThat(testDriver.close(), is(true));
- }
-
- @Test
- public void requireThatRequestFilterChainRetainsFilters() {
- final RequestFilter requestFilter1 = mock(RequestFilter.class);
- final RequestFilter requestFilter2 = mock(RequestFilter.class);
-
- verify(requestFilter1, never()).refer();
- verify(requestFilter2, never()).refer();
- final ResourceReference reference1 = mock(ResourceReference.class);
- final ResourceReference reference2 = mock(ResourceReference.class);
- when(requestFilter1.refer()).thenReturn(reference1);
- when(requestFilter2.refer()).thenReturn(reference2);
- final RequestFilter chain = RequestFilterChain.newInstance(requestFilter1, requestFilter2);
- verify(requestFilter1, times(1)).refer();
- verify(requestFilter2, times(1)).refer();
-
- verify(reference1, never()).close();
- verify(reference2, never()).close();
- chain.release();
- verify(reference1, times(1)).close();
- verify(reference2, times(1)).close();
- }
-
- @Test
- public void requireThatRequestFilterChainIsRun() throws Exception {
- final RequestFilter requestFilter1 = mock(RequestFilter.class);
- final RequestFilter requestFilter2 = mock(RequestFilter.class);
- final RequestFilter requestFilterChain = RequestFilterChain.newInstance(requestFilter1, requestFilter2);
- final HttpRequest request = null;
- final ResponseHandler responseHandler = null;
- requestFilterChain.filter(request, responseHandler);
- verify(requestFilter1).filter(isNull(), any(ResponseHandler.class));
- verify(requestFilter2).filter(isNull(), any(ResponseHandler.class));
- }
-
- @Test
- public void requireThatRequestFilterChainCallsFilterWithOriginalRequest() throws Exception {
- final RequestFilter requestFilter = mock(RequestFilter.class);
- final RequestFilter requestFilterChain = RequestFilterChain.newInstance(requestFilter);
- final HttpRequest request = mock(HttpRequest.class);
- final ResponseHandler responseHandler = null;
- requestFilterChain.filter(request, responseHandler);
-
- // Check that the filter is called with the same request argument as the chain was,
- // in a manner that allows the request object to be wrapped.
- final ArgumentCaptor<HttpRequest> requestCaptor = ArgumentCaptor.forClass(HttpRequest.class);
- verify(requestFilter).filter(requestCaptor.capture(), isNull());
- verify(request, never()).getUri();
- requestCaptor.getValue().getUri();
- verify(request, times(1)).getUri();
- }
-
- @Test
- public void requireThatRequestFilterChainCallsFilterWithOriginalResponseHandler() throws Exception {
- final RequestFilter requestFilter = mock(RequestFilter.class);
- final RequestFilter requestFilterChain = RequestFilterChain.newInstance(requestFilter);
- final HttpRequest request = null;
- final ResponseHandler responseHandler = mock(ResponseHandler.class);
- requestFilterChain.filter(request, responseHandler);
-
- // Check that the filter is called with the same response handler argument as the chain was,
- // in a manner that allows the handler object to be wrapped.
- final ArgumentCaptor<ResponseHandler> responseHandlerCaptor = ArgumentCaptor.forClass(ResponseHandler.class);
- verify(requestFilter).filter(isNull(), responseHandlerCaptor.capture());
- verify(responseHandler, never()).handleResponse(any(Response.class));
- responseHandlerCaptor.getValue().handleResponse(mock(Response.class));
- verify(responseHandler, times(1)).handleResponse(any(Response.class));
- }
-
- @Test
- public void requireThatRequestFilterCanTerminateChain() throws Exception {
- final RequestFilter requestFilter1 = new RespondForbiddenFilter();
- final RequestFilter requestFilter2 = mock(RequestFilter.class);
- final RequestFilter requestFilterChain = RequestFilterChain.newInstance(requestFilter1, requestFilter2);
- final HttpRequest request = null;
- final ResponseHandler responseHandler = mock(ResponseHandler.class);
- when(responseHandler.handleResponse(any(Response.class))).thenReturn(mock(ContentChannel.class));
-
- requestFilterChain.filter(request, responseHandler);
-
- verify(requestFilter2, never()).filter(any(HttpRequest.class), any(ResponseHandler.class));
-
- final ArgumentCaptor<Response> responseCaptor = ArgumentCaptor.forClass(Response.class);
- verify(responseHandler).handleResponse(responseCaptor.capture());
- assertThat(responseCaptor.getValue().getStatus(), is(Response.Status.FORBIDDEN));
- }
-
- @Test
- public void requireThatResponseFilterChainRetainsFilters() {
- final ResponseFilter responseFilter1 = mock(ResponseFilter.class);
- final ResponseFilter responseFilter2 = mock(ResponseFilter.class);
-
- verify(responseFilter1, never()).refer();
- verify(responseFilter2, never()).refer();
- final ResourceReference reference1 = mock(ResourceReference.class);
- final ResourceReference reference2 = mock(ResourceReference.class);
- when(responseFilter1.refer()).thenReturn(reference1);
- when(responseFilter2.refer()).thenReturn(reference2);
- final ResponseFilter chain = ResponseFilterChain.newInstance(responseFilter1, responseFilter2);
- verify(responseFilter1, times(1)).refer();
- verify(responseFilter2, times(1)).refer();
-
- verify(reference1, never()).close();
- verify(reference2, never()).close();
- chain.release();
- verify(reference1, times(1)).close();
- verify(reference2, times(1)).close();
- }
-
- @Test
- public void requireThatResponseFilterChainIsRun() {
- final ResponseFilter responseFilter1 = new ResponseHeaderFilter("foo", "bar");
- final ResponseFilter responseFilter2 = mock(ResponseFilter.class);
- final int statusCode = Response.Status.BAD_GATEWAY;
- final Response response = new Response(statusCode);
- final Request request = null;
-
- ResponseFilterChain.newInstance(responseFilter1, responseFilter2).filter(response, request);
-
- final ArgumentCaptor<Response> responseCaptor = ArgumentCaptor.forClass(Response.class);
- verify(responseFilter2).filter(responseCaptor.capture(), isNull());
- assertThat(responseCaptor.getValue().getStatus(), is(statusCode));
- assertThat(responseCaptor.getValue().headers().getFirst("foo"), is("bar"));
-
- assertThat(response.getStatus(), is(statusCode));
- assertThat(response.headers().getFirst("foo"), is("bar"));
- }
-
- @Test
- public void requireThatDefaultRequestFilterChainIsRunIfNoOtherFilterChainMatches() throws IOException, InterruptedException {
- RequestFilter filterWithBinding = mock(RequestFilter.class);
- RequestFilter defaultFilter = mock(RequestFilter.class);
- String defaultFilterId = "default-request-filter";
- FilterBindings filterBindings = new FilterBindings.Builder()
- .addRequestFilter("my-request-filter", filterWithBinding)
- .addRequestFilterBinding("my-request-filter", "http://*/filtered/*")
- .addRequestFilter(defaultFilterId, defaultFilter)
- .setRequestFilterDefaultForPort(defaultFilterId, 0)
- .build();
- MyRequestHandler requestHandler = new MyRequestHandler();
- TestDriver testDriver = newDriver(requestHandler, filterBindings);
-
- testDriver.client().get("/status.html");
-
- assertThat(requestHandler.awaitInvocation(), is(true));
- verify(defaultFilter, times(1)).filter(any(HttpRequest.class), any(ResponseHandler.class));
- verify(filterWithBinding, never()).filter(any(HttpRequest.class), any(ResponseHandler.class));
-
- assertThat(testDriver.close(), is(true));
- }
-
- @Test
- public void requireThatDefaultResponseFilterChainIsRunIfNoOtherFilterChainMatches() throws IOException, InterruptedException {
- ResponseFilter filterWithBinding = mock(ResponseFilter.class);
- ResponseFilter defaultFilter = mock(ResponseFilter.class);
- String defaultFilterId = "default-response-filter";
- FilterBindings filterBindings = new FilterBindings.Builder()
- .addResponseFilter("my-response-filter", filterWithBinding)
- .addResponseFilterBinding("my-response-filter", "http://*/filtered/*")
- .addResponseFilter(defaultFilterId, defaultFilter)
- .setResponseFilterDefaultForPort(defaultFilterId, 0)
- .build();
- MyRequestHandler requestHandler = new MyRequestHandler();
- TestDriver testDriver = newDriver(requestHandler, filterBindings);
-
- testDriver.client().get("/status.html");
-
- assertThat(requestHandler.awaitInvocation(), is(true));
- verify(defaultFilter, times(1)).filter(any(Response.class), any(Request.class));
- verify(filterWithBinding, never()).filter(any(Response.class), any(Request.class));
-
- assertThat(testDriver.close(), is(true));
- }
-
- @Test
- public void requireThatRequestFilterWithBindingMatchHasPrecedenceOverDefaultFilter() throws IOException, InterruptedException {
- RequestFilterMockBase filterWithBinding = mock(RequestFilterMockBase.class);
- RequestFilterMockBase defaultFilter = mock(RequestFilterMockBase.class);
- String defaultFilterId = "default-request-filter";
- FilterBindings filterBindings = new FilterBindings.Builder()
- .addRequestFilter("my-request-filter", filterWithBinding)
- .addRequestFilterBinding("my-request-filter", "http://*/filtered/*")
- .addRequestFilter(defaultFilterId, defaultFilter)
- .setRequestFilterDefaultForPort(defaultFilterId, 0)
- .build();
- MyRequestHandler requestHandler = new MyRequestHandler();
- TestDriver testDriver = newDriver(requestHandler, filterBindings);
-
- testDriver.client().get("/filtered/status.html");
-
- assertThat(requestHandler.awaitInvocation(), is(true));
- verify(defaultFilter, never()).filter(any(HttpRequest.class), any(ResponseHandler.class));
- verify(filterWithBinding).filter(any(HttpRequest.class), any(ResponseHandler.class));
-
- assertThat(testDriver.close(), is(true));
- }
-
- @Test
- public void requireThatResponseFilterWithBindingMatchHasPrecedenceOverDefaultFilter() throws IOException, InterruptedException {
- ResponseFilter filterWithBinding = mock(ResponseFilter.class);
- ResponseFilter defaultFilter = mock(ResponseFilter.class);
- String defaultFilterId = "default-response-filter";
- FilterBindings filterBindings = new FilterBindings.Builder()
- .addResponseFilter("my-response-filter", filterWithBinding)
- .addResponseFilterBinding("my-response-filter", "http://*/filtered/*")
- .addResponseFilter(defaultFilterId, defaultFilter)
- .setResponseFilterDefaultForPort(defaultFilterId, 0)
- .build();
- MyRequestHandler requestHandler = new MyRequestHandler();
- TestDriver testDriver = newDriver(requestHandler, filterBindings);
-
- testDriver.client().get("/filtered/status.html");
-
- assertThat(requestHandler.awaitInvocation(), is(true));
- verify(defaultFilter, never()).filter(any(Response.class), any(Request.class));
- verify(filterWithBinding, times(1)).filter(any(Response.class), any(Request.class));
-
- assertThat(testDriver.close(), is(true));
- }
-
- @Test
- public void requireThatMetricAreReported() throws IOException, InterruptedException {
- FilterBindings filterBindings = new FilterBindings.Builder()
- .addRequestFilter("my-request-filter", mock(RequestFilter.class))
- .addRequestFilterBinding("my-request-filter", "http://*/*")
- .build();
- MetricConsumerMock metricConsumerMock = new MetricConsumerMock();
- MyRequestHandler requestHandler = new MyRequestHandler();
- TestDriver testDriver = newDriver(requestHandler, filterBindings, metricConsumerMock, false);
-
- testDriver.client().get("/status.html");
- assertThat(requestHandler.awaitInvocation(), is(true));
- verify(metricConsumerMock.mockitoMock())
- .add(MetricDefinitions.FILTERING_REQUEST_HANDLED, 1L, MetricConsumerMock.STATIC_CONTEXT);
- verify(metricConsumerMock.mockitoMock(), never())
- .add(MetricDefinitions.FILTERING_REQUEST_UNHANDLED, 1L, MetricConsumerMock.STATIC_CONTEXT);
- verify(metricConsumerMock.mockitoMock(), never())
- .add(MetricDefinitions.FILTERING_RESPONSE_HANDLED, 1L, MetricConsumerMock.STATIC_CONTEXT);
- verify(metricConsumerMock.mockitoMock())
- .add(MetricDefinitions.FILTERING_RESPONSE_UNHANDLED, 1L, MetricConsumerMock.STATIC_CONTEXT);
- assertThat(testDriver.close(), is(true));
- }
-
- @Test
- public void requireThatStrictFilteringRejectsRequestsNotMatchingFilterChains() throws IOException {
- RequestFilter filter = mock(RequestFilter.class);
- FilterBindings filterBindings = new FilterBindings.Builder()
- .addRequestFilter("my-request-filter", filter)
- .addRequestFilterBinding("my-request-filter", "http://*/filtered/*")
- .build();
- MyRequestHandler requestHandler = new MyRequestHandler();
- TestDriver testDriver = newDriver(requestHandler, filterBindings, new MetricConsumerMock(), true);
-
- testDriver.client().get("/unfiltered/")
- .expectStatusCode(is(Response.Status.FORBIDDEN))
- .expectContent(containsString("Request did not match any request filter chain"));
- verify(filter, never()).filter(any(), any());
- assertThat(testDriver.close(), is(true));
- }
-
- private static TestDriver newDriver(MyRequestHandler requestHandler, FilterBindings filterBindings) {
- return newDriver(requestHandler, filterBindings, new MetricConsumerMock(), false);
- }
-
- private static TestDriver newDriver(
- MyRequestHandler requestHandler,
- FilterBindings filterBindings,
- MetricConsumerMock metricConsumer,
- boolean strictFiltering) {
- return TestDriver.newInstance(
- JettyHttpServer.class,
- requestHandler,
- newFilterModule(filterBindings, metricConsumer, strictFiltering));
- }
-
- private static com.google.inject.Module newFilterModule(
- FilterBindings filterBindings, MetricConsumerMock metricConsumer, boolean strictFiltering) {
- return Modules.combine(
- new AbstractModule() {
- @Override
- protected void configure() {
-
- bind(FilterBindings.class).toInstance(filterBindings);
- bind(ServerConfig.class).toInstance(new ServerConfig(new ServerConfig.Builder().strictFiltering(strictFiltering)));
- bind(ConnectorConfig.class).toInstance(new ConnectorConfig(new ConnectorConfig.Builder()));
- bind(ServletPathsConfig.class).toInstance(new ServletPathsConfig(new ServletPathsConfig.Builder()));
- bind(ConnectionLog.class).toInstance(new VoidConnectionLog());
- bind(RequestLog.class).toInstance(new VoidRequestLog());
- }
- },
- new ConnectorFactoryRegistryModule(),
- metricConsumer.asGuiceModule());
- }
-
- private static abstract class RequestFilterMockBase extends AbstractResource implements RequestFilter {}
- private static abstract class ResponseFilterMockBase extends AbstractResource implements ResponseFilter {}
-
- private static class MyRequestHandler extends AbstractRequestHandler {
- private final CountDownLatch invocationLatch = new CountDownLatch(1);
- private final AtomicReference<Map<String, List<String>>> headerCopy = new AtomicReference<>(null);
-
- @Override
- public ContentChannel handleRequest(final Request request, final ResponseHandler handler) {
- try {
- headerCopy.set(new HashMap<String, List<String>>(request.headers()));
- ResponseDispatch.newInstance(Response.Status.OK).dispatch(handler);
- return null;
- } finally {
- invocationLatch.countDown();
- }
- }
-
- public boolean hasBeenInvokedYet() {
- return invocationLatch.getCount() == 0L;
- }
-
- public boolean awaitInvocation() throws InterruptedException {
- return invocationLatch.await(60, TimeUnit.SECONDS);
- }
-
- public Map<String, List<String>> getHeaderMap() {
- return headerCopy.get();
- }
- }
-
- private static class RespondForbiddenFilter extends AbstractResource implements RequestFilter {
- @Override
- public void filter(final HttpRequest request, final ResponseHandler handler) {
- ResponseDispatch.newInstance(Response.Status.FORBIDDEN).dispatch(handler);
- }
- }
-
- private static class ThrowingRequestFilter extends AbstractResource implements RequestFilter {
- @Override
- public void filter(final HttpRequest request, final ResponseHandler handler) {
- throw new RuntimeException();
- }
- }
-
- private static class ThrowingResponseFilter extends AbstractResource implements ResponseFilter {
- @Override
- public void filter(final Response response, final Request request) {
- throw new RuntimeException();
- }
- }
-
- private static class HeaderRequestFilter extends AbstractResource implements RequestFilter {
- private final String key;
- private final String val;
-
- public HeaderRequestFilter(final String key, final String val) {
- this.key = key;
- this.val = val;
- }
-
- @Override
- public void filter(final HttpRequest request, final ResponseHandler handler) {
- request.headers().add(key, val);
- }
- }
-
- private static class HeaderResponseFilter extends AbstractResource implements ResponseFilter {
- private final String key;
- private final String val;
-
- public HeaderResponseFilter(final String key, final String val) {
- this.key = key;
- this.val = val;
- }
-
- @Override
- public void filter(final Response response, final Request request) {
- response.headers().add(key, val);
- }
- }
-
- public class NullCompletionHandlerFilter extends AbstractResource implements RequestFilter {
- private final int responseStatus;
- private final String responseMessage;
-
- public NullCompletionHandlerFilter(final int responseStatus, final String responseMessage) {
- this.responseStatus = responseStatus;
- this.responseMessage = responseMessage;
- }
-
- @Override
- public void filter(final HttpRequest request, final ResponseHandler responseHandler) {
- final HttpResponse response = HttpResponse.newInstance(responseStatus);
- final ContentChannel channel = responseHandler.handleResponse(response);
- final CompletionHandler completionHandler = null;
- channel.write(ByteBuffer.wrap(responseMessage.getBytes()), completionHandler);
- channel.close(null);
- }
- }
-}
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpRequestFactoryTest.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpRequestFactoryTest.java
deleted file mode 100644
index 9c1348004ee..00000000000
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpRequestFactoryTest.java
+++ /dev/null
@@ -1,204 +0,0 @@
-// 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.server.jetty;
-
-import com.google.inject.Key;
-import com.yahoo.jdisc.Container;
-import com.yahoo.jdisc.References;
-import com.yahoo.jdisc.ResourceReference;
-import com.yahoo.jdisc.Response;
-import com.yahoo.jdisc.handler.RequestHandler;
-import com.yahoo.jdisc.http.ConnectorConfig;
-import com.yahoo.jdisc.http.HttpRequest;
-import com.yahoo.jdisc.service.CurrentContainer;
-import org.eclipse.jetty.server.HttpConnection;
-import org.junit.Test;
-
-import javax.servlet.http.HttpServletRequest;
-import java.net.URI;
-
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-/**
- * @author Steinar Knutsen
- * @author bjorncs
- */
-public class HttpRequestFactoryTest {
-
- private static final int LOCAL_PORT = 80;
-
- @Test
- public void testLegalURIs() {
- {
- URI uri = HttpRequestFactory.getUri(createMockRequest("https", "host", null, null));
- assertEquals("https", uri.getScheme());
- assertEquals("host", uri.getHost());
- assertEquals("", uri.getRawPath());
- assertNull(uri.getRawQuery());
- }
- {
- URI uri = HttpRequestFactory.getUri(createMockRequest("https", "host", "", ""));
- assertEquals("https", uri.getScheme());
- assertEquals("host", uri.getHost());
- assertEquals("", uri.getRawPath());
- assertEquals("", uri.getRawQuery());
- }
- {
- URI uri = HttpRequestFactory.getUri(createMockRequest("http", "host.a1-2-3", "", ""));
- assertEquals("http", uri.getScheme());
- assertEquals("host.a1-2-3", uri.getHost());
- assertEquals("", uri.getRawPath());
- assertEquals("", uri.getRawQuery());
- }
- {
- URI uri = HttpRequestFactory.getUri(createMockRequest("https", "host", "/:1/../1=.", ""));
- assertEquals("https", uri.getScheme());
- assertEquals("host", uri.getHost());
- assertEquals("/:1/../1=.", uri.getRawPath());
- assertEquals("", uri.getRawQuery());
- }
- {
- URI uri = HttpRequestFactory.getUri(createMockRequest("https", "host", "", "a=/../&?="));
- assertEquals("https", uri.getScheme());
- assertEquals("host", uri.getHost());
- assertEquals("", uri.getRawPath());
- assertEquals("a=/../&?=", uri.getRawQuery());
- }
- }
-
- @Test
- public void testIllegalQuery() {
- try {
- HttpRequestFactory.newJDiscRequest(
- new MockContainer(),
- createMockRequest("http", "example.com", "/search", "query=\"contains_quotes\""));
- fail("Above statement should throw");
- } catch (RequestException e) {
- assertThat(e.getResponseStatus(), is(Response.Status.BAD_REQUEST));
- }
- }
-
- @Test
- public final void illegal_host_throws_requestexception1() {
- try {
- HttpRequestFactory.newJDiscRequest(
- new MockContainer(),
- createMockRequest("http", "?", "/foo", ""));
- fail("Above statement should throw");
- } catch (RequestException e) {
- assertThat(e.getResponseStatus(), is(Response.Status.BAD_REQUEST));
- }
- }
-
- @Test
- public final void illegal_host_throws_requestexception2() {
- try {
- HttpRequestFactory.newJDiscRequest(
- new MockContainer(),
- createMockRequest("http", ".", "/foo", ""));
- fail("Above statement should throw");
- } catch (RequestException e) {
- assertThat(e.getResponseStatus(), is(Response.Status.BAD_REQUEST));
- }
- }
-
- @Test
- public final void illegal_host_throws_requestexception3() {
- try {
- HttpRequestFactory.newJDiscRequest(
- new MockContainer(),
- createMockRequest("http", "*", "/foo", ""));
- fail("Above statement should throw");
- } catch (RequestException e) {
- assertThat(e.getResponseStatus(), is(Response.Status.BAD_REQUEST));
- }
- }
-
- @Test
- public final void illegal_unicode_in_query_throws_requestexception() {
- try {
- HttpRequestFactory.newJDiscRequest(
- new MockContainer(),
- createMockRequest("http", "example.com", "/search", "query=%c0%ae"));
- fail("Above statement should throw");
- } catch (RequestException e) {
- assertThat(e.getResponseStatus(), is(Response.Status.BAD_REQUEST));
- assertThat(e.getMessage(), equalTo("URL violates RFC 2396: Not valid UTF8! byte C0 in state 0"));
- }
- }
-
- @Test
- public void request_uri_uses_local_port() {
- HttpRequest request = HttpRequestFactory.newJDiscRequest(
- new MockContainer(),
- createMockRequest("https", "example.com", "/search", "query=value"));
- assertEquals(LOCAL_PORT, request.getUri().getPort());
- }
-
- private static HttpServletRequest createMockRequest(String scheme, String serverName, String path, String queryString) {
- HttpServletRequest request = mock(HttpServletRequest.class);
- HttpConnection connection = mock(HttpConnection.class);
- JDiscServerConnector connector = mock(JDiscServerConnector.class);
- when(connector.connectorConfig()).thenReturn(new ConnectorConfig(new ConnectorConfig.Builder().listenPort(LOCAL_PORT)));
- when(connector.getLocalPort()).thenReturn(LOCAL_PORT);
- when(connection.getCreatedTimeStamp()).thenReturn(System.currentTimeMillis());
- when(connection.getConnector()).thenReturn(connector);
- when(request.getAttribute("org.eclipse.jetty.server.HttpConnection")).thenReturn(connection);
- when(request.getProtocol()).thenReturn("HTTP/1.1");
- when(request.getScheme()).thenReturn(scheme);
- when(request.getServerName()).thenReturn(serverName);
- when(request.getRemoteAddr()).thenReturn("127.0.0.1");
- when(request.getRemotePort()).thenReturn(1234);
- when(request.getLocalPort()).thenReturn(LOCAL_PORT);
- when(request.getMethod()).thenReturn("GET");
- when(request.getQueryString()).thenReturn(queryString);
- when(request.getRequestURI()).thenReturn(path);
- return request;
- }
-
- private static final class MockContainer implements CurrentContainer {
-
- @Override
- public Container newReference(URI uri) {
- return new Container() {
-
- @Override
- public RequestHandler resolveHandler(com.yahoo.jdisc.Request request) {
- return null;
- }
-
- @Override
- public <T> T getInstance(Key<T> tKey) {
- return null;
- }
-
- @Override
- public <T> T getInstance(Class<T> tClass) {
- return null;
- }
-
- @Override
- public ResourceReference refer() {
- return References.NOOP_REFERENCE;
- }
-
- @Override
- public void release() {
-
- }
-
- @Override
- public long currentTimeMillis() {
- return 0;
- }
- };
- }
- }
-
-}
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpResponseStatisticsCollectorTest.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpResponseStatisticsCollectorTest.java
deleted file mode 100644
index bb92d75bed5..00000000000
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpResponseStatisticsCollectorTest.java
+++ /dev/null
@@ -1,221 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.jdisc.http.server.jetty;
-
-import com.yahoo.jdisc.http.server.jetty.HttpResponseStatisticsCollector.StatisticsEntry;
-import org.eclipse.jetty.http.HttpFields;
-import org.eclipse.jetty.http.HttpURI;
-import org.eclipse.jetty.http.HttpVersion;
-import org.eclipse.jetty.http.MetaData;
-import org.eclipse.jetty.http.MetaData.Response;
-import org.eclipse.jetty.server.AbstractConnector;
-import org.eclipse.jetty.server.Connector;
-import org.eclipse.jetty.server.HttpChannel;
-import org.eclipse.jetty.server.HttpConfiguration;
-import org.eclipse.jetty.server.HttpTransport;
-import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.Server;
-import org.eclipse.jetty.server.handler.AbstractHandler;
-import org.eclipse.jetty.util.Callback;
-import org.junit.Before;
-import org.junit.Test;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.List;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.equalTo;
-
-/**
- * @author ollivir
- */
-public class HttpResponseStatisticsCollectorTest {
-
- private Connector connector;
- private List<String> monitoringPaths = List.of("/status.html");
- private List<String> searchPaths = List.of("/search");
- private HttpResponseStatisticsCollector collector = new HttpResponseStatisticsCollector(monitoringPaths, searchPaths);
- private int httpResponseCode = 500;
-
- @Test
- public void statistics_are_aggregated_by_category() {
- testRequest("http", 300, "GET");
- testRequest("http", 301, "GET");
- testRequest("http", 200, "GET");
-
- var stats = collector.takeStatistics();
- assertStatisticsEntryPresent(stats, "http", "GET", MetricDefinitions.RESPONSES_2XX, 1L);
- assertStatisticsEntryPresent(stats, "http", "GET", MetricDefinitions.RESPONSES_3XX, 2L);
- }
-
- @Test
- public void statistics_are_grouped_by_http_method_and_scheme() {
- testRequest("http", 200, "GET");
- testRequest("http", 200, "PUT");
- testRequest("http", 200, "POST");
- testRequest("http", 200, "POST");
- testRequest("http", 404, "GET");
- testRequest("https", 404, "GET");
- testRequest("https", 200, "POST");
- testRequest("https", 200, "POST");
- testRequest("https", 200, "POST");
- testRequest("https", 200, "POST");
-
- var stats = collector.takeStatistics();
- assertStatisticsEntryPresent(stats, "http", "GET", MetricDefinitions.RESPONSES_2XX, 1L);
- assertStatisticsEntryPresent(stats, "http", "GET", MetricDefinitions.RESPONSES_4XX, 1L);
- assertStatisticsEntryPresent(stats, "http", "PUT", MetricDefinitions.RESPONSES_2XX, 1L);
- assertStatisticsEntryPresent(stats, "http", "POST", MetricDefinitions.RESPONSES_2XX, 2L);
- assertStatisticsEntryPresent(stats, "https", "GET", MetricDefinitions.RESPONSES_4XX, 1L);
- assertStatisticsEntryPresent(stats, "https", "POST", MetricDefinitions.RESPONSES_2XX, 4L);
- }
-
- @Test
- public void statistics_include_grouped_and_single_statuscodes() {
- testRequest("http", 401, "GET");
- testRequest("http", 404, "GET");
- testRequest("http", 403, "GET");
-
- var stats = collector.takeStatistics();
- assertStatisticsEntryPresent(stats, "http", "GET", MetricDefinitions.RESPONSES_4XX, 3L);
- assertStatisticsEntryPresent(stats, "http", "GET", MetricDefinitions.RESPONSES_401, 1L);
- assertStatisticsEntryPresent(stats, "http", "GET", MetricDefinitions.RESPONSES_403, 1L);
-
- }
-
- @Test
- public void retrieving_statistics_resets_the_counters() {
- testRequest("http", 200, "GET");
- testRequest("http", 200, "GET");
-
- var stats = collector.takeStatistics();
- assertStatisticsEntryPresent(stats, "http", "GET", MetricDefinitions.RESPONSES_2XX, 2L);
-
- testRequest("http", 200, "GET");
-
- stats = collector.takeStatistics();
- assertStatisticsEntryPresent(stats, "http", "GET", MetricDefinitions.RESPONSES_2XX, 1L);
- }
-
- @Test
- public void statistics_include_request_type_dimension() {
- testRequest("http", 200, "GET", "/search");
- testRequest("http", 200, "POST", "/search");
- testRequest("http", 200, "POST", "/feed");
- testRequest("http", 200, "GET", "/status.html?foo=bar");
-
- var stats = collector.takeStatistics();
- assertStatisticsEntryWithRequestTypePresent(stats, "http", "GET", MetricDefinitions.RESPONSES_2XX, "monitoring", 1L);
- assertStatisticsEntryWithRequestTypePresent(stats, "http", "GET", MetricDefinitions.RESPONSES_2XX, "read", 1L);
- assertStatisticsEntryWithRequestTypePresent(stats, "http", "POST", MetricDefinitions.RESPONSES_2XX, "read", 1L);
- assertStatisticsEntryWithRequestTypePresent(stats, "http", "POST", MetricDefinitions.RESPONSES_2XX, "write", 1L);
-
- testRequest("http", 200, "GET");
-
- stats = collector.takeStatistics();
- assertStatisticsEntryPresent(stats, "http", "GET", MetricDefinitions.RESPONSES_2XX, 1L);
- }
-
- @Test
- public void request_type_can_be_set_explicitly() {
- testRequest("http", 200, "GET", "/search", com.yahoo.jdisc.Request.RequestType.WRITE);
-
- var stats = collector.takeStatistics();
- assertStatisticsEntryWithRequestTypePresent(stats, "http", "GET", MetricDefinitions.RESPONSES_2XX, "write", 1L);
- }
-
- @Before
- public void initializeCollector() throws Exception {
- Server server = new Server();
- connector = new AbstractConnector(server, null, null, null, 0) {
- @Override
- protected void accept(int acceptorID) throws IOException, InterruptedException {
- }
-
- @Override
- public Object getTransport() {
- return null;
- }
- };
- collector.setHandler(new AbstractHandler() {
- @Override
- public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
- throws IOException, ServletException {
- baseRequest.setHandled(true);
- baseRequest.getResponse().setStatus(httpResponseCode);
- }
- });
- server.setHandler(collector);
- server.start();
- }
-
- private Request testRequest(String scheme, int responseCode, String httpMethod) {
- return testRequest(scheme, responseCode, httpMethod, "foo/bar");
- }
- private Request testRequest(String scheme, int responseCode, String httpMethod, String path) {
- return testRequest(scheme, responseCode, httpMethod, path, null);
- }
- private Request testRequest(String scheme, int responseCode, String httpMethod, String path,
- com.yahoo.jdisc.Request.RequestType explicitRequestType) {
- HttpChannel channel = new HttpChannel(connector, new HttpConfiguration(), null, new DummyTransport());
- MetaData.Request metaData = new MetaData.Request(httpMethod, new HttpURI(scheme + "://" + path), HttpVersion.HTTP_1_1, new HttpFields());
- Request req = channel.getRequest();
- if (explicitRequestType != null)
- req.setAttribute("requestType", explicitRequestType);
- req.setMetaData(metaData);
-
- this.httpResponseCode = responseCode;
- channel.handle();
- return req;
- }
-
- private static void assertStatisticsEntryPresent(List<StatisticsEntry> result, String scheme, String method, String name, long expectedValue) {
- long value = result.stream()
- .filter(entry -> entry.method.equals(method) && entry.scheme.equals(scheme) && entry.name.equals(name))
- .mapToLong(entry -> entry.value)
- .findAny()
- .orElseThrow(() -> new AssertionError(String.format("Not matching entry in result (scheme=%s, method=%s, name=%s)", scheme, method, name)));
- assertThat(value, equalTo(expectedValue));
- }
-
- private static void assertStatisticsEntryWithRequestTypePresent(List<StatisticsEntry> result, String scheme, String method, String name, String requestType, long expectedValue) {
- long value = result.stream()
- .filter(entry -> entry.method.equals(method) && entry.scheme.equals(scheme) && entry.name.equals(name) && entry.requestType.equals(requestType))
- .mapToLong(entry -> entry.value)
- .reduce(Long::sum)
- .orElseThrow(() -> new AssertionError(String.format("Not matching entry in result (scheme=%s, method=%s, name=%s, type=%s)", scheme, method, name, requestType)));
- assertThat(value, equalTo(expectedValue));
- }
-
- private final class DummyTransport implements HttpTransport {
- @Override
- public void send(Response info, boolean head, ByteBuffer content, boolean lastContent, Callback callback) {
- callback.succeeded();
- }
-
- @Override
- public boolean isPushSupported() {
- return false;
- }
-
- @Override
- public boolean isOptimizedForDirectBuffers() {
- return false;
- }
-
- @Override
- public void push(MetaData.Request request) {
- }
-
- @Override
- public void onCompleted() {
- }
-
- @Override
- public void abort(Throwable failure) {
- }
- }
-}
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerConformanceTest.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerConformanceTest.java
deleted file mode 100644
index 5659dfc2d3c..00000000000
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerConformanceTest.java
+++ /dev/null
@@ -1,847 +0,0 @@
-// 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.server.jetty;
-
-import com.google.inject.AbstractModule;
-import com.google.inject.Module;
-import com.google.inject.util.Modules;
-import com.yahoo.container.logging.ConnectionLog;
-import com.yahoo.container.logging.RequestLog;
-import com.yahoo.jdisc.http.ServerConfig;
-import com.yahoo.jdisc.http.ServletPathsConfig;
-import com.yahoo.jdisc.http.guiceModules.ConnectorFactoryRegistryModule;
-import com.yahoo.jdisc.test.ServerProviderConformanceTest;
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpVersion;
-import org.apache.http.ProtocolVersion;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.client.methods.HttpUriRequest;
-import org.apache.http.entity.StringEntity;
-import org.apache.http.impl.client.HttpClientBuilder;
-import org.apache.http.util.EntityUtils;
-import org.hamcrest.Description;
-import org.hamcrest.Matcher;
-import org.hamcrest.TypeSafeMatcher;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-import java.net.URI;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.Collections;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.regex.Pattern;
-
-import static com.yahoo.jdisc.Response.Status.INTERNAL_SERVER_ERROR;
-import static com.yahoo.jdisc.Response.Status.NOT_FOUND;
-import static com.yahoo.jdisc.Response.Status.OK;
-import static org.apache.http.HttpStatus.SC_INTERNAL_SERVER_ERROR;
-import static org.apache.http.HttpStatus.SC_NOT_FOUND;
-import static org.cthul.matchers.CthulMatchers.containsPattern;
-import static org.cthul.matchers.CthulMatchers.matchesPattern;
-import static org.hamcrest.CoreMatchers.any;
-import static org.hamcrest.CoreMatchers.anyOf;
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-/**
- * @author Simon Thoresen Hult
- */
-public class HttpServerConformanceTest extends ServerProviderConformanceTest {
-
- private static final Logger log = Logger.getLogger(HttpServerConformanceTest.class.getName());
-
- private static final String REQUEST_CONTENT = "myRequestContent";
- private static final String RESPONSE_CONTENT = "myResponseContent";
-
- @SuppressWarnings("LoggerInitializedWithForeignClass")
- private static Logger httpRequestDispatchLogger = Logger.getLogger(HttpRequestDispatch.class.getName());
- private static Level httpRequestDispatchLoggerOriginalLevel;
-
- /*
- * Reduce logging of every stack trace for {@link ServerProviderConformanceTest.ConformanceException} thrown.
- * This makes the log more readable and the test faster as well.
- */
- @BeforeClass
- public static void reduceExcessiveLogging() {
- httpRequestDispatchLoggerOriginalLevel = httpRequestDispatchLogger.getLevel();
- httpRequestDispatchLogger.setLevel(Level.SEVERE);
- }
-
- @AfterClass
- public static void restoreExcessiveLogging() {
- httpRequestDispatchLogger.setLevel(httpRequestDispatchLoggerOriginalLevel);
- }
-
- @AfterClass
- public static void reportDiagnostics() {
- System.out.println(
- "After " + HttpServerConformanceTest.class.getSimpleName()
- + ": #threads=" + Thread.getAllStackTraces().size());
- }
-
- @Override
- @Test
- public void testContainerNotReadyException() throws Throwable {
- new TestRunner().expect(errorWithReason(is(SC_INTERNAL_SERVER_ERROR), containsString("Container not ready.")))
- .execute();
- }
-
- @Override
- @Test
- public void testBindingSetNotFoundException() throws Throwable {
- new TestRunner().expect(errorWithReason(is(SC_NOT_FOUND), containsString("No binding set named 'unknown'.")))
- .execute();
- }
-
- @Override
- @Test
- public void testNoBindingSetSelectedException() throws Throwable {
- final Pattern reasonPattern = Pattern.compile(".*No binding set selected for URI 'http://.+/status.html'\\.");
- new TestRunner().expect(errorWithReason(is(SC_INTERNAL_SERVER_ERROR), matchesPattern(reasonPattern)))
- .execute();
- }
-
- @Override
- @Test
- public void testBindingNotFoundException() throws Throwable {
- final Pattern contentPattern = Pattern.compile("No binding for URI 'http://.+/status.html'\\.");
- new TestRunner().expect(errorWithReason(is(NOT_FOUND), containsPattern(contentPattern)))
- .execute();
- }
-
- @Override
- @Test
- public void testRequestHandlerWithSyncCloseResponse() throws Throwable {
- new TestRunner().expect(success())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestHandlerWithSyncWriteResponse() throws Throwable {
- new TestRunner().expect(success())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestHandlerWithSyncHandleResponse() throws Throwable {
- new TestRunner().expect(success())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestHandlerWithAsyncHandleResponse() throws Throwable {
- new TestRunner().expect(success())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestException() throws Throwable {
- new TestRunner().expect(serverError())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestExceptionWithSyncCloseResponse() throws Throwable {
- new TestRunner().expect(success())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestExceptionWithSyncWriteResponse() throws Throwable {
- new TestRunner().expect(success())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestNondeterministicExceptionWithSyncHandleResponse() throws Throwable {
- new TestRunner().expect(anyOf(success(), serverError()))
- .execute();
- }
-
- @Override
- @Test
- public void testRequestExceptionBeforeResponseWriteWithSyncHandleResponse() throws Throwable {
- new TestRunner().expect(serverError())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestExceptionAfterResponseWriteWithSyncHandleResponse() throws Throwable {
- new TestRunner().expect(success())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestNondeterministicExceptionWithAsyncHandleResponse() throws Throwable {
- new TestRunner().expect(anyOf(successNoContent(), serverError()))
- .execute();
- }
-
- @Override
- @Test
- public void testRequestExceptionBeforeResponseWriteWithAsyncHandleResponse() throws Throwable {
- new TestRunner().expect(serverError())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestExceptionAfterResponseCloseNoContentWithAsyncHandleResponse() throws Throwable {
- new TestRunner().expect(successNoContent())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestExceptionAfterResponseWriteWithAsyncHandleResponse() throws Throwable {
- new TestRunner().expect(success())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentWriteWithSyncCompletion() throws Throwable {
- new TestRunner().expect(success())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentWriteWithAsyncCompletion() throws Throwable {
- new TestRunner().expect(success())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentWriteWithNondeterministicSyncFailure() throws Throwable {
- new TestRunner().expect(anyOf(success(), serverError()))
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentWriteWithSyncFailureBeforeResponseWrite() throws Throwable {
- new TestRunner().expect(serverError())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentWriteWithSyncFailureAfterResponseWrite() throws Throwable {
- new TestRunner().expect(success())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentWriteWithNondeterministicAsyncFailure() throws Throwable {
- new TestRunner().expect(anyOf(success(), serverError()))
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentWriteWithAsyncFailureBeforeResponseWrite() throws Throwable {
- new TestRunner().expect(serverError())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentWriteWithAsyncFailureAfterResponseWrite() throws Throwable {
- new TestRunner().expect(success())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentWriteWithAsyncFailureAfterResponseCloseNoContent() throws Throwable {
- new TestRunner().expect(successNoContent())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentWriteNondeterministicException() throws Throwable {
- new TestRunner().expect(anyOf(success(), serverError(), successNoContent()))
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentWriteExceptionBeforeResponseWrite() throws Throwable {
- new TestRunner().expect(serverError())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentWriteExceptionAfterResponseWrite() throws Throwable {
- new TestRunner().expect(success())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentWriteExceptionAfterResponseCloseNoContent() throws Throwable {
- new TestRunner().expect(successNoContent())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentWriteNondeterministicExceptionWithSyncCompletion() throws Throwable {
- new TestRunner().expect(anyOf(success(), serverError()))
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentWriteExceptionBeforeResponseWriteWithSyncCompletion() throws Throwable {
- new TestRunner().expect(serverError())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentWriteExceptionAfterResponseWriteWithSyncCompletion() throws Throwable {
- new TestRunner().expect(anyOf(success(), successNoContent()))
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentWriteExceptionAfterResponseCloseNoContentWithSyncCompletion() throws Throwable {
- new TestRunner().expect(anyOf(success(), successNoContent()))
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentWriteNondeterministicExceptionWithAsyncCompletion() throws Throwable {
- new TestRunner()
- .expect(anyOf(success(), successNoContent(), serverError()))
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentWriteExceptionBeforeResponseWriteWithAsyncCompletion() throws Throwable {
- new TestRunner().expect(serverError())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentWriteExceptionAfterResponseWriteWithAsyncCompletion() throws Throwable {
- new TestRunner().expect(success())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentWriteExceptionAfterResponseCloseNoContentWithAsyncCompletion() throws Throwable {
- new TestRunner().expect(successNoContent())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentWriteExceptionWithNondeterministicSyncFailure() throws Throwable {
- new TestRunner().expect(anyOf(success(), successNoContent(), serverError()))
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentWriteExceptionWithSyncFailureBeforeResponseWrite() throws Throwable {
- new TestRunner().expect(serverError())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentWriteExceptionWithSyncFailureAfterResponseWrite() throws Throwable {
- new TestRunner().expect(success())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentWriteExceptionWithSyncFailureAfterResponseCloseNoContent() throws Throwable {
- new TestRunner().expect(successNoContent())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentWriteExceptionWithNondeterministicAsyncFailure() throws Throwable {
- new TestRunner().expect(anyOf(success(), serverError()))
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentWriteExceptionWithAsyncFailureBeforeResponseWrite() throws Throwable {
- new TestRunner().expect(serverError())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentWriteExceptionWithAsyncFailureAfterResponseWrite() throws Throwable {
- new TestRunner().expect(success())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentWriteExceptionWithAsyncFailureAfterResponseCloseNoContent() throws Throwable {
- new TestRunner().expect(successNoContent())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentCloseWithSyncCompletion() throws Throwable {
- new TestRunner().expect(success())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentCloseWithAsyncCompletion() throws Throwable {
- new TestRunner().expect(success())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentCloseWithNondeterministicSyncFailure() throws Throwable {
- new TestRunner().expect(anyOf(success(), successNoContent(), serverError()))
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentCloseWithSyncFailureBeforeResponseWrite() throws Throwable {
- new TestRunner().expect(serverError())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentCloseWithSyncFailureAfterResponseWrite() throws Throwable {
- new TestRunner().expect(success())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentCloseWithSyncFailureAfterResponseCloseNoContent() throws Throwable {
- new TestRunner().expect(successNoContent())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentCloseWithNondeterministicAsyncFailure() throws Throwable {
- new TestRunner().expect(anyOf(success(), successNoContent(), serverError()))
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentCloseWithAsyncFailureBeforeResponseWrite() throws Throwable {
- new TestRunner().expect(serverError())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentCloseWithAsyncFailureAfterResponseWrite() throws Throwable {
- new TestRunner().expect(success())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentCloseWithAsyncFailureAfterResponseCloseNoContent() throws Throwable {
- new TestRunner().expect(successNoContent())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentCloseNondeterministicException() throws Throwable {
- new TestRunner().expect(anyOf(success(), successNoContent(), serverError()))
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentCloseExceptionBeforeResponseWrite() throws Throwable {
- new TestRunner().expect(serverError())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentCloseExceptionAfterResponseWrite() throws Throwable {
- new TestRunner().expect(success())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentCloseExceptionAfterResponseCloseNoContent() throws Throwable {
- new TestRunner().expect(successNoContent())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentCloseNondeterministicExceptionWithSyncCompletion() throws Throwable {
- new TestRunner().expect(anyOf(success(), serverError(), successNoContent()))
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentCloseExceptionBeforeResponseWriteWithSyncCompletion() throws Throwable {
- new TestRunner().expect(serverError())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentCloseExceptionAfterResponseWriteWithSyncCompletion() throws Throwable {
- new TestRunner().expect(success())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentCloseExceptionAfterResponseCloseNoContentWithSyncCompletion() throws Throwable {
- new TestRunner().expect(successNoContent())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentCloseNondeterministicExceptionWithAsyncCompletion() throws Throwable {
- new TestRunner().expect(anyOf(success(), serverError(), successNoContent()))
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentCloseExceptionBeforeResponseWriteWithAsyncCompletion() throws Throwable {
- new TestRunner().expect(serverError())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentCloseExceptionAfterResponseWriteWithAsyncCompletion() throws Throwable {
- new TestRunner().expect(success())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentCloseExceptionAfterResponseCloseNoContentWithAsyncCompletion() throws Throwable {
- new TestRunner().expect(successNoContent())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentCloseNondeterministicExceptionWithSyncFailure() throws Throwable {
- new TestRunner().expect(anyOf(success(), successNoContent(), serverError()))
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentCloseExceptionBeforeResponseWriteWithSyncFailure() throws Throwable {
- new TestRunner().expect(serverError())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentCloseExceptionAfterResponseWriteWithSyncFailure() throws Throwable {
- new TestRunner().expect(success())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentCloseExceptionAfterResponseCloseNoContentWithSyncFailure() throws Throwable {
- new TestRunner().expect(successNoContent())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentCloseNondeterministicExceptionWithAsyncFailure() throws Throwable {
- new TestRunner().expect(anyOf(success(), successNoContent(), serverError()))
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentCloseExceptionBeforeResponseWriteWithAsyncFailure() throws Throwable {
- new TestRunner().expect(serverError())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentCloseExceptionAfterResponseWriteWithAsyncFailure() throws Throwable {
- new TestRunner().expect(success())
- .execute();
- }
-
- @Override
- @Test
- public void testRequestContentCloseExceptionAfterResponseCloseNoContentWithAsyncFailure() throws Throwable {
- new TestRunner().expect(successNoContent())
- .execute();
- }
-
- @Override
- @Test
- public void testResponseWriteCompletionException() throws Throwable {
- new TestRunner().expect(success())
- .execute();
- }
-
- @Override
- @Test
- public void testResponseCloseCompletionException() throws Throwable {
- new TestRunner().expect(success())
- .execute();
- }
-
- @Override
- @Test
- public void testResponseCloseCompletionExceptionNoContent() throws Throwable {
- new TestRunner().expect(successNoContent())
- .execute();
- }
-
- private static Matcher<ResponseGist> success() {
- final Matcher<Integer> expectedStatusCode = is(OK);
- final Matcher<String> expectedReasonPhrase = is("OK");
- final Matcher<String> expectedContent = is(RESPONSE_CONTENT);
- return responseMatcher(expectedStatusCode, expectedReasonPhrase, expectedContent);
- }
-
- private static Matcher<ResponseGist> successNoContent() {
- final Matcher<Integer> expectedStatusCode = is(OK);
- final Matcher<String> expectedReasonPhrase = is("OK");
- final Matcher<String> expectedContent = is("");
- return responseMatcher(expectedStatusCode, expectedReasonPhrase, expectedContent);
- }
-
- private static Matcher<ResponseGist> serverError() {
- final Matcher<Integer> expectedStatusCode = is(INTERNAL_SERVER_ERROR);
- final Matcher<String> expectedReasonPhrase = any(String.class);
- final Matcher<String> expectedContent = containsString(ConformanceException.class.getSimpleName());
- return responseMatcher(expectedStatusCode, expectedReasonPhrase, expectedContent);
- }
-
- private static Matcher<ResponseGist> errorWithReason(
- final Matcher<Integer> expectedStatusCode, final Matcher<String> expectedReasonPhrase) {
- final Matcher<String> expectedContent = any(String.class);
- return responseMatcher(expectedStatusCode, expectedReasonPhrase, expectedContent);
- }
-
- private static Matcher<ResponseGist> responseMatcher(
- final Matcher<Integer> expectedStatusCode,
- final Matcher<String> expectedReasonPhrase,
- final Matcher<String> expectedContent) {
- return new TypeSafeMatcher<ResponseGist>() {
- @Override
- public void describeTo(final Description description) {
- description.appendText("status code ");
- expectedStatusCode.describeTo(description);
- description.appendText(", reason ");
- expectedReasonPhrase.describeTo(description);
- description.appendText(" and content ");
- expectedContent.describeTo(description);
- }
-
- @Override
- protected void describeMismatchSafely(
- final ResponseGist response, final Description mismatchDescription) {
- mismatchDescription.appendText(" status code was ").appendValue(response.getStatusCode())
- .appendText(", reason was ").appendValue(response.getReasonPhrase())
- .appendText(" and content was ").appendValue(response.getContent());
- }
-
- @Override
- protected boolean matchesSafely(final ResponseGist response) {
- return expectedStatusCode.matches(response.getStatusCode())
- && expectedReasonPhrase.matches(response.getReasonPhrase())
- && expectedContent.matches(response.getContent());
- }
- };
- }
-
- private static class ResponseGist {
- private final int statusCode;
- private final String content;
- private String reasonPhrase;
-
- public ResponseGist(int statusCode, String reasonPhrase, String content) {
- this.statusCode = statusCode;
- this.reasonPhrase = reasonPhrase;
- this.content = content;
- }
-
- public int getStatusCode() {
- return statusCode;
- }
-
- public String getContent() {
- return content;
- }
-
- public String getReasonPhrase() {
- return reasonPhrase;
- }
-
- @Override
- public String toString() {
- return "ResponseGist {"
- + " statusCode=" + statusCode
- + " reasonPhrase=" + reasonPhrase
- + " content=" + content
- + " }";
- }
- }
-
- private class TestRunner implements Adapter<JettyHttpServer, ClientProxy, Future<HttpResponse>> {
-
- private Matcher<ResponseGist> expectedResponse = null;
- HttpVersion requestVersion;
- private final ExecutorService executorService = Executors.newSingleThreadExecutor();
-
- void execute() throws Throwable {
- requestVersion = HttpVersion.HTTP_1_0;
- runTest(this);
-
- requestVersion = HttpVersion.HTTP_1_1;
- runTest(this);
-
- executorService.shutdown();
- }
-
- TestRunner expect(final Matcher<ResponseGist> matcher) {
- expectedResponse = matcher;
- return this;
- }
-
- @Override
- public Module newConfigModule() {
- return Modules.combine(
- new AbstractModule() {
- @Override
- protected void configure() {
- bind(FilterBindings.class)
- .toInstance(new FilterBindings.Builder().build());
- bind(ServerConfig.class)
- .toInstance(new ServerConfig(new ServerConfig.Builder()));
- bind(ServletPathsConfig.class)
- .toInstance(new ServletPathsConfig(new ServletPathsConfig.Builder()));
- bind(ConnectionLog.class)
- .toInstance(new VoidConnectionLog());
- bind(RequestLog.class)
- .toInstance(new VoidRequestLog());
- }
- },
- new ConnectorFactoryRegistryModule());
- }
-
- @Override
- public Class<JettyHttpServer> getServerProviderClass() {
- return JettyHttpServer.class;
- }
-
- @Override
- public ClientProxy newClient(final JettyHttpServer server) throws Throwable {
- return new ClientProxy(server.getListenPort(), requestVersion);
- }
-
- @Override
- public Future<HttpResponse> executeRequest(
- final ClientProxy client,
- final boolean withRequestContent) throws Throwable {
- final HttpUriRequest request;
- final URI requestUri = URI.create("http://localhost:" + client.listenPort + "/status.html");
- if (!withRequestContent) {
- HttpGet httpGet = new HttpGet(requestUri);
- httpGet.setProtocolVersion(client.requestVersion);
- request = httpGet;
- } else {
- final HttpPost post = new HttpPost(requestUri);
- post.setEntity(new StringEntity(REQUEST_CONTENT, StandardCharsets.UTF_8));
- post.setProtocolVersion(client.requestVersion);
- request = post;
- }
- log.fine(() -> "executorService:"
- + " .isShutDown()=" + executorService.isShutdown()
- + " .isTerminated()=" + executorService.isTerminated());
- return executorService.submit(() -> client.delegate.execute(request));
- }
-
- @Override
- public Iterable<ByteBuffer> newResponseContent() {
- return Collections.singleton(StandardCharsets.UTF_8.encode(RESPONSE_CONTENT));
- }
-
- @Override
- public void validateResponse(final Future<HttpResponse> responseFuture) throws Throwable {
- final HttpResponse response = responseFuture.get();
- final ResponseGist responseGist = new ResponseGist(
- response.getStatusLine().getStatusCode(),
- response.getStatusLine().getReasonPhrase(),
- EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8));
- assertThat(responseGist, expectedResponse);
- }
- }
-
- private static class ClientProxy {
-
- final HttpClient delegate;
- final int listenPort;
- final ProtocolVersion requestVersion;
-
- ClientProxy(final int listenPort, final HttpVersion requestVersion) {
- this.delegate = HttpClientBuilder.create().build();
- this.requestVersion = requestVersion;
- this.listenPort = listenPort;
- }
- }
-}
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerTest.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerTest.java
deleted file mode 100644
index c00525a3ddc..00000000000
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerTest.java
+++ /dev/null
@@ -1,1201 +0,0 @@
-// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.jdisc.http.server.jetty;
-
-import com.google.inject.AbstractModule;
-import com.google.inject.Module;
-import com.yahoo.container.logging.ConnectionLog;
-import com.yahoo.container.logging.ConnectionLogEntry;
-import com.yahoo.container.logging.ConnectionLogEntry.SslHandshakeFailure.ExceptionEntry;
-import com.yahoo.container.logging.RequestLog;
-import com.yahoo.container.logging.RequestLogEntry;
-import com.yahoo.jdisc.References;
-import com.yahoo.jdisc.Request;
-import com.yahoo.jdisc.Response;
-import com.yahoo.jdisc.application.BindingSetSelector;
-import com.yahoo.jdisc.application.MetricConsumer;
-import com.yahoo.jdisc.handler.AbstractRequestHandler;
-import com.yahoo.jdisc.handler.CompletionHandler;
-import com.yahoo.jdisc.handler.ContentChannel;
-import com.yahoo.jdisc.handler.RequestHandler;
-import com.yahoo.jdisc.handler.ResponseDispatch;
-import com.yahoo.jdisc.handler.ResponseHandler;
-import com.yahoo.jdisc.http.ConnectorConfig;
-import com.yahoo.jdisc.http.ConnectorConfig.Throttling;
-import com.yahoo.jdisc.http.Cookie;
-import com.yahoo.jdisc.http.HttpRequest;
-import com.yahoo.jdisc.http.HttpResponse;
-import com.yahoo.jdisc.http.ServerConfig;
-import com.yahoo.jdisc.http.server.jetty.TestDrivers.TlsClientAuth;
-import com.yahoo.jdisc.service.BindingSetNotFoundException;
-import com.yahoo.security.KeyUtils;
-import com.yahoo.security.Pkcs10Csr;
-import com.yahoo.security.Pkcs10CsrBuilder;
-import com.yahoo.security.SslContextBuilder;
-import com.yahoo.security.X509CertificateBuilder;
-import com.yahoo.security.X509CertificateUtils;
-import com.yahoo.security.tls.TlsContext;
-import org.apache.http.conn.ssl.NoopHostnameVerifier;
-import org.apache.http.entity.ContentType;
-import org.apache.http.entity.mime.FormBodyPart;
-import org.apache.http.entity.mime.content.StringBody;
-import org.assertj.core.api.Assertions;
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.client.ProxyProtocolClientConnectionFactory.V1;
-import org.eclipse.jetty.client.ProxyProtocolClientConnectionFactory.V2;
-import org.eclipse.jetty.client.api.ContentResponse;
-import org.eclipse.jetty.server.handler.AbstractHandlerContainer;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TemporaryFolder;
-
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLException;
-import javax.net.ssl.SSLHandshakeException;
-import javax.security.auth.x500.X500Principal;
-import java.io.IOException;
-import java.math.BigInteger;
-import java.net.BindException;
-import java.net.URI;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.cert.X509Certificate;
-import java.time.Duration;
-import java.time.Instant;
-import java.time.temporal.ChronoUnit;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-import java.util.UUID;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-import java.util.regex.Pattern;
-
-import static com.yahoo.jdisc.Response.Status.GATEWAY_TIMEOUT;
-import static com.yahoo.jdisc.Response.Status.INTERNAL_SERVER_ERROR;
-import static com.yahoo.jdisc.Response.Status.NOT_FOUND;
-import static com.yahoo.jdisc.Response.Status.OK;
-import static com.yahoo.jdisc.Response.Status.REQUEST_URI_TOO_LONG;
-import static com.yahoo.jdisc.Response.Status.UNAUTHORIZED;
-import static com.yahoo.jdisc.Response.Status.UNSUPPORTED_MEDIA_TYPE;
-import static com.yahoo.jdisc.http.HttpHeaders.Names.CONNECTION;
-import static com.yahoo.jdisc.http.HttpHeaders.Names.CONTENT_TYPE;
-import static com.yahoo.jdisc.http.HttpHeaders.Names.COOKIE;
-import static com.yahoo.jdisc.http.HttpHeaders.Names.X_DISABLE_CHUNKING;
-import static com.yahoo.jdisc.http.HttpHeaders.Values.APPLICATION_X_WWW_FORM_URLENCODED;
-import static com.yahoo.jdisc.http.HttpHeaders.Values.CLOSE;
-import static com.yahoo.jdisc.http.server.jetty.SimpleHttpClient.ResponseValidator;
-import static com.yahoo.security.KeyAlgorithm.EC;
-import static com.yahoo.security.SignatureAlgorithm.SHA256_WITH_ECDSA;
-import static org.cthul.matchers.CthulMatchers.containsPattern;
-import static org.cthul.matchers.CthulMatchers.matchesPattern;
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.startsWith;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.anyOf;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeTrue;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-/**
- * @author Oyvind Bakksjo
- * @author Simon Thoresen Hult
- * @author bjorncs
- */
-public class HttpServerTest {
-
- private static final Logger log = Logger.getLogger(HttpServerTest.class.getName());
-
- @Rule
- public TemporaryFolder tmpFolder = new TemporaryFolder();
-
- @Test
- public void requireThatServerCanListenToRandomPort() throws Exception {
- final TestDriver driver = TestDrivers.newInstance(mockRequestHandler());
- assertNotEquals(0, driver.server().getListenPort());
- assertTrue(driver.close());
- }
-
- @Test
- public void requireThatServerCanNotListenToBoundPort() throws Exception {
- final TestDriver driver = TestDrivers.newInstance(mockRequestHandler());
- try {
- TestDrivers.newConfiguredInstance(
- mockRequestHandler(),
- new ServerConfig.Builder(),
- new ConnectorConfig.Builder()
- .listenPort(driver.server().getListenPort())
- );
- } catch (final Throwable t) {
- assertThat(t.getCause(), instanceOf(BindException.class));
- }
- assertTrue(driver.close());
- }
-
- @Test
- public void requireThatBindingSetNotFoundReturns404() throws Exception {
- final TestDriver driver = TestDrivers.newConfiguredInstance(
- mockRequestHandler(),
- new ServerConfig.Builder()
- .developerMode(true),
- new ConnectorConfig.Builder(),
- newBindingSetSelector("unknown"));
- driver.client().get("/status.html")
- .expectStatusCode(is(NOT_FOUND))
- .expectContent(containsPattern(Pattern.compile(
- Pattern.quote(BindingSetNotFoundException.class.getName()) +
- ": No binding set named &apos;unknown&apos;\\.\n\tat .+",
- Pattern.DOTALL | Pattern.MULTILINE)));
- assertTrue(driver.close());
- }
-
- @Test
- public void requireThatTooLongInitLineReturns414() throws Exception {
- final TestDriver driver = TestDrivers.newConfiguredInstance(
- mockRequestHandler(),
- new ServerConfig.Builder(),
- new ConnectorConfig.Builder()
- .requestHeaderSize(1));
- driver.client().get("/status.html")
- .expectStatusCode(is(REQUEST_URI_TOO_LONG));
- assertTrue(driver.close());
- }
-
- @Test
- public void requireThatAccessLogIsCalledForRequestRejectedByJetty() throws Exception {
- BlockingQueueRequestLog requestLogMock = new BlockingQueueRequestLog();
- final TestDriver driver = TestDrivers.newConfiguredInstance(
- mockRequestHandler(),
- new ServerConfig.Builder(),
- new ConnectorConfig.Builder().requestHeaderSize(1),
- binder -> binder.bind(RequestLog.class).toInstance(requestLogMock));
- driver.client().get("/status.html")
- .expectStatusCode(is(REQUEST_URI_TOO_LONG));
- RequestLogEntry entry = requestLogMock.poll(Duration.ofSeconds(30));
- assertEquals(414, entry.statusCode().getAsInt());
- assertThat(driver.close(), is(true));
- }
-
- @Test
- public void requireThatServerCanEcho() throws Exception {
- final TestDriver driver = TestDrivers.newInstance(new EchoRequestHandler());
- driver.client().get("/status.html")
- .expectStatusCode(is(OK));
- assertTrue(driver.close());
- }
-
- @Test
- public void requireThatServerCanEchoCompressed() throws Exception {
- final TestDriver driver = TestDrivers.newInstance(new EchoRequestHandler());
- SimpleHttpClient client = driver.newClient(true);
- client.get("/status.html")
- .expectStatusCode(is(OK));
- assertTrue(driver.close());
- }
-
- @Test
- public void requireThatServerCanHandleMultipleRequests() throws Exception {
- final TestDriver driver = TestDrivers.newInstance(new EchoRequestHandler());
- driver.client().get("/status.html")
- .expectStatusCode(is(OK));
- driver.client().get("/status.html")
- .expectStatusCode(is(OK));
- assertTrue(driver.close());
- }
-
- @Test
- public void requireThatFormPostWorks() throws Exception {
- final TestDriver driver = TestDrivers.newInstance(new ParameterPrinterRequestHandler());
- final String requestContent = generateContent('a', 30);
- final ResponseValidator response =
- driver.client().newPost("/status.html")
- .addHeader(CONTENT_TYPE, APPLICATION_X_WWW_FORM_URLENCODED)
- .setContent(requestContent)
- .execute();
- response.expectStatusCode(is(OK))
- .expectContent(startsWith('{' + requestContent + "=[]}"));
- assertTrue(driver.close());
- }
-
- @Test
- public void requireThatFormPostDoesNotRemoveContentByDefault() throws Exception {
- final TestDriver driver = TestDrivers.newInstance(new ParameterPrinterRequestHandler());
- final ResponseValidator response =
- driver.client().newPost("/status.html")
- .addHeader(CONTENT_TYPE, APPLICATION_X_WWW_FORM_URLENCODED)
- .setContent("foo=bar")
- .execute();
- response.expectStatusCode(is(OK))
- .expectContent(is("{foo=[bar]}foo=bar"));
- assertTrue(driver.close());
- }
-
- @Test
- public void requireThatFormPostKeepsContentWhenConfiguredTo() throws Exception {
- final TestDriver driver = newDriverWithFormPostContentRemoved(new ParameterPrinterRequestHandler(), false);
- final ResponseValidator response =
- driver.client().newPost("/status.html")
- .addHeader(CONTENT_TYPE, APPLICATION_X_WWW_FORM_URLENCODED)
- .setContent("foo=bar")
- .execute();
- response.expectStatusCode(is(OK))
- .expectContent(is("{foo=[bar]}foo=bar"));
- assertTrue(driver.close());
- }
-
- @Test
- public void requireThatFormPostRemovesContentWhenConfiguredTo() throws Exception {
- final TestDriver driver = newDriverWithFormPostContentRemoved(new ParameterPrinterRequestHandler(), true);
- final ResponseValidator response =
- driver.client().newPost("/status.html")
- .addHeader(CONTENT_TYPE, APPLICATION_X_WWW_FORM_URLENCODED)
- .setContent("foo=bar")
- .execute();
- response.expectStatusCode(is(OK))
- .expectContent(is("{foo=[bar]}"));
- assertTrue(driver.close());
- }
-
- @Test
- public void requireThatFormPostWithCharsetSpecifiedWorks() throws Exception {
- final TestDriver driver = TestDrivers.newInstance(new ParameterPrinterRequestHandler());
- final String requestContent = generateContent('a', 30);
- final ResponseValidator response =
- driver.client().newPost("/status.html")
- .addHeader(X_DISABLE_CHUNKING, "true")
- .addHeader(CONTENT_TYPE, APPLICATION_X_WWW_FORM_URLENCODED + ";charset=UTF-8")
- .setContent(requestContent)
- .execute();
- response.expectStatusCode(is(OK))
- .expectContent(startsWith('{' + requestContent + "=[]}"));
- assertTrue(driver.close());
- }
-
- @Test
- public void requireThatEmptyFormPostWorks() throws Exception {
- final TestDriver driver = TestDrivers.newInstance(new ParameterPrinterRequestHandler());
- final ResponseValidator response =
- driver.client().newPost("/status.html")
- .addHeader(CONTENT_TYPE, APPLICATION_X_WWW_FORM_URLENCODED)
- .execute();
- response.expectStatusCode(is(OK))
- .expectContent(is("{}"));
- assertTrue(driver.close());
- }
-
- @Test
- public void requireThatFormParametersAreParsed() throws Exception {
- final TestDriver driver = TestDrivers.newInstance(new ParameterPrinterRequestHandler());
- final ResponseValidator response =
- driver.client().newPost("/status.html")
- .addHeader(CONTENT_TYPE, APPLICATION_X_WWW_FORM_URLENCODED)
- .setContent("a=b&c=d")
- .execute();
- response.expectStatusCode(is(OK))
- .expectContent(startsWith("{a=[b], c=[d]}"));
- assertTrue(driver.close());
- }
-
- @Test
- public void requireThatUriParametersAreParsed() throws Exception {
- final TestDriver driver = TestDrivers.newInstance(new ParameterPrinterRequestHandler());
- final ResponseValidator response =
- driver.client().newPost("/status.html?a=b&c=d")
- .addHeader(CONTENT_TYPE, APPLICATION_X_WWW_FORM_URLENCODED)
- .execute();
- response.expectStatusCode(is(OK))
- .expectContent(is("{a=[b], c=[d]}"));
- assertTrue(driver.close());
- }
-
- @Test
- public void requireThatFormAndUriParametersAreMerged() throws Exception {
- final TestDriver driver = TestDrivers.newInstance(new ParameterPrinterRequestHandler());
- final ResponseValidator response =
- driver.client().newPost("/status.html?a=b&c=d1")
- .addHeader(CONTENT_TYPE, APPLICATION_X_WWW_FORM_URLENCODED)
- .setContent("c=d2&e=f")
- .execute();
- response.expectStatusCode(is(OK))
- .expectContent(startsWith("{a=[b], c=[d1, d2], e=[f]}"));
- assertTrue(driver.close());
- }
-
- @Test
- public void requireThatFormCharsetIsHonored() throws Exception {
- final TestDriver driver = newDriverWithFormPostContentRemoved(new ParameterPrinterRequestHandler(), true);
- final ResponseValidator response =
- driver.client().newPost("/status.html")
- .addHeader(CONTENT_TYPE, APPLICATION_X_WWW_FORM_URLENCODED + ";charset=ISO-8859-1")
- .setBinaryContent(new byte[]{66, (byte) 230, 114, 61, 98, 108, (byte) 229})
- .execute();
- response.expectStatusCode(is(OK))
- .expectContent(is("{B\u00e6r=[bl\u00e5]}"));
- assertTrue(driver.close());
- }
-
- @Test
- public void requireThatUnknownFormCharsetIsTreatedAsBadRequest() throws Exception {
- final TestDriver driver = TestDrivers.newInstance(new ParameterPrinterRequestHandler());
- final ResponseValidator response =
- driver.client().newPost("/status.html")
- .addHeader(CONTENT_TYPE, APPLICATION_X_WWW_FORM_URLENCODED + ";charset=FLARBA-GARBA-7")
- .setContent("a=b")
- .execute();
- response.expectStatusCode(is(UNSUPPORTED_MEDIA_TYPE));
- assertTrue(driver.close());
- }
-
- @Test
- public void requireThatFormPostWithPercentEncodedContentIsDecoded() throws Exception {
- final TestDriver driver = TestDrivers.newInstance(new ParameterPrinterRequestHandler());
- final ResponseValidator response =
- driver.client().newPost("/status.html")
- .addHeader(CONTENT_TYPE, APPLICATION_X_WWW_FORM_URLENCODED)
- .setContent("%20%3D%C3%98=%22%25+")
- .execute();
- response.expectStatusCode(is(OK))
- .expectContent(startsWith("{ =\u00d8=[\"% ]}"));
- assertTrue(driver.close());
- }
-
- @Test
- public void requireThatFormPostWithThrowingHandlerIsExceptionSafe() throws Exception {
- final TestDriver driver = TestDrivers.newInstance(new ThrowingHandler());
- final ResponseValidator response =
- driver.client().newPost("/status.html")
- .addHeader(CONTENT_TYPE, APPLICATION_X_WWW_FORM_URLENCODED)
- .setContent("a=b")
- .execute();
- response.expectStatusCode(is(INTERNAL_SERVER_ERROR));
- assertTrue(driver.close());
- }
-
- @Test
- public void requireThatMultiPostWorks() throws Exception {
- // This is taken from tcpdump of bug 5433352 and reassembled here to see that httpserver passes things on.
- final String startTxtContent = "this is a test for POST.";
- final String updaterConfContent
- = "identifier = updater\n"
- + "server_type = gds\n";
- final TestDriver driver = TestDrivers.newInstance(new EchoRequestHandler());
- final ResponseValidator response =
- driver.client().newPost("/status.html")
- .setMultipartContent(
- newFileBody("", "start.txt", startTxtContent),
- newFileBody("", "updater.conf", updaterConfContent))
- .execute();
- response.expectStatusCode(is(OK))
- .expectContent(containsString(startTxtContent))
- .expectContent(containsString(updaterConfContent));
- }
-
- @Test
- public void requireThatRequestCookiesAreReceived() throws Exception {
- final TestDriver driver = TestDrivers.newInstance(new CookiePrinterRequestHandler());
- final ResponseValidator response =
- driver.client().newPost("/status.html")
- .addHeader(COOKIE, "foo=bar")
- .execute();
- response.expectStatusCode(is(OK))
- .expectContent(containsString("[foo=bar]"));
- assertTrue(driver.close());
- }
-
- @Test
- public void requireThatSetCookieHeaderIsCorrect() throws Exception {
- final TestDriver driver = TestDrivers.newInstance(new CookieSetterRequestHandler(
- new Cookie("foo", "bar")
- .setDomain(".localhost")
- .setHttpOnly(true)
- .setPath("/foopath")
- .setSecure(true)));
- driver.client().get("/status.html")
- .expectStatusCode(is(OK))
- .expectHeader("Set-Cookie",
- is("foo=bar; Path=/foopath; Domain=.localhost; Secure; HttpOnly"));
- assertTrue(driver.close());
- }
-
- @Test
- public void requireThatTimeoutWorks() throws Exception {
- final UnresponsiveHandler requestHandler = new UnresponsiveHandler();
- final TestDriver driver = TestDrivers.newInstance(requestHandler);
- driver.client().get("/status.html")
- .expectStatusCode(is(GATEWAY_TIMEOUT));
- ResponseDispatch.newInstance(OK).dispatch(requestHandler.responseHandler);
- assertTrue(driver.close());
- }
-
- // Header with no value is disallowed by https://tools.ietf.org/html/rfc7230#section-3.2
- // Details in https://github.com/eclipse/jetty.project/issues/1116
- @Test
- public void requireThatHeaderWithNullValueIsOmitted() throws Exception {
- final TestDriver driver = TestDrivers.newInstance(new EchoWithHeaderRequestHandler("X-Foo", null));
- driver.client().get("/status.html")
- .expectStatusCode(is(OK))
- .expectNoHeader("X-Foo");
- assertTrue(driver.close());
- }
-
- // Header with empty value is allowed by https://tools.ietf.org/html/rfc7230#section-3.2
- // Details in https://github.com/eclipse/jetty.project/issues/1116
- @Test
- public void requireThatHeaderWithEmptyValueIsAllowed() throws Exception {
- final TestDriver driver = TestDrivers.newInstance(new EchoWithHeaderRequestHandler("X-Foo", ""));
- driver.client().get("/status.html")
- .expectStatusCode(is(OK))
- .expectHeader("X-Foo", is(""));
- assertTrue(driver.close());
- }
-
- @Test
- public void requireThatNoConnectionHeaderMeansKeepAliveInHttp11KeepAliveDisabled() throws Exception {
- final TestDriver driver = TestDrivers.newInstance(new EchoWithHeaderRequestHandler(CONNECTION, CLOSE));
- driver.client().get("/status.html")
- .expectHeader(CONNECTION, is(CLOSE));
- assertThat(driver.close(), is(true));
- }
-
- @Test
- public void requireThatConnectionIsClosedAfterXRequests() throws Exception {
- final int MAX_KEEPALIVE_REQUESTS = 100;
- final TestDriver driver = TestDrivers.newConfiguredInstance(new EchoRequestHandler(),
- new ServerConfig.Builder(),
- new ConnectorConfig.Builder().maxRequestsPerConnection(MAX_KEEPALIVE_REQUESTS));
- for (int i = 0; i < MAX_KEEPALIVE_REQUESTS - 1; i++) {
- driver.client().get("/status.html")
- .expectStatusCode(is(OK))
- .expectNoHeader(CONNECTION);
- }
- driver.client().get("/status.html")
- .expectStatusCode(is(OK))
- .expectHeader(CONNECTION, is(CLOSE));
- assertTrue(driver.close());
- }
-
- @Test
- public void requireThatServerCanRespondToSslRequest() throws Exception {
- Path privateKeyFile = tmpFolder.newFile().toPath();
- Path certificateFile = tmpFolder.newFile().toPath();
- generatePrivateKeyAndCertificate(privateKeyFile, certificateFile);
-
- final TestDriver driver = TestDrivers.newInstanceWithSsl(new EchoRequestHandler(), certificateFile, privateKeyFile, TlsClientAuth.WANT);
- driver.client().get("/status.html")
- .expectStatusCode(is(OK));
- assertTrue(driver.close());
- }
-
- @Test
- public void requireThatTlsClientAuthenticationEnforcerRejectsRequestsForNonWhitelistedPaths() throws IOException {
- Path privateKeyFile = tmpFolder.newFile().toPath();
- Path certificateFile = tmpFolder.newFile().toPath();
- generatePrivateKeyAndCertificate(privateKeyFile, certificateFile);
- TestDriver driver = TestDrivers.newInstanceWithSsl(new EchoRequestHandler(), certificateFile, privateKeyFile, TlsClientAuth.WANT);
-
- SSLContext trustStoreOnlyCtx = new SslContextBuilder()
- .withTrustStore(certificateFile)
- .build();
-
- new SimpleHttpClient(trustStoreOnlyCtx, driver.server().getListenPort(), false)
- .get("/dummy.html")
- .expectStatusCode(is(UNAUTHORIZED));
-
- assertTrue(driver.close());
- }
-
- @Test
- public void requireThatTlsClientAuthenticationEnforcerAllowsRequestForWhitelistedPaths() throws IOException {
- Path privateKeyFile = tmpFolder.newFile().toPath();
- Path certificateFile = tmpFolder.newFile().toPath();
- generatePrivateKeyAndCertificate(privateKeyFile, certificateFile);
- TestDriver driver = TestDrivers.newInstanceWithSsl(new EchoRequestHandler(), certificateFile, privateKeyFile, TlsClientAuth.WANT);
-
- SSLContext trustStoreOnlyCtx = new SslContextBuilder()
- .withTrustStore(certificateFile)
- .build();
-
- new SimpleHttpClient(trustStoreOnlyCtx, driver.server().getListenPort(), false)
- .get("/status.html")
- .expectStatusCode(is(OK));
-
- assertTrue(driver.close());
- }
-
- @Test
- public void requireThatConnectedAtReturnsNonZero() throws Exception {
- final TestDriver driver = TestDrivers.newInstance(new ConnectedAtRequestHandler());
- driver.client().get("/status.html")
- .expectStatusCode(is(OK))
- .expectContent(matchesPattern("\\d{13,}"));
- assertThat(driver.close(), is(true));
- }
-
- @Test
- public void requireThatGzipEncodingRequestsAreAutomaticallyDecompressed() throws Exception {
- TestDriver driver = TestDrivers.newInstance(new ParameterPrinterRequestHandler());
- String requestContent = generateContent('a', 30);
- ResponseValidator response = driver.client().newPost("/status.html")
- .addHeader(CONTENT_TYPE, APPLICATION_X_WWW_FORM_URLENCODED)
- .setGzipContent(requestContent)
- .execute();
- response.expectStatusCode(is(OK))
- .expectContent(startsWith('{' + requestContent + "=[]}"));
- assertTrue(driver.close());
- }
-
- @Test
- public void requireThatResponseStatsAreCollected() throws Exception {
- RequestTypeHandler handler = new RequestTypeHandler();
- TestDriver driver = TestDrivers.newInstance(handler);
- HttpResponseStatisticsCollector statisticsCollector = ((AbstractHandlerContainer) driver.server().server().getHandler())
- .getChildHandlerByClass(HttpResponseStatisticsCollector.class);
-
- {
- List<HttpResponseStatisticsCollector.StatisticsEntry> stats = statisticsCollector.takeStatistics();
- assertEquals(0, stats.size());
- }
-
- {
- driver.client().newPost("/status.html").execute();
- var entry = waitForStatistics(statisticsCollector);
- assertEquals("http", entry.scheme);
- assertEquals("POST", entry.method);
- assertEquals("http.status.2xx", entry.name);
- assertEquals("write", entry.requestType);
- assertEquals(1, entry.value);
- }
-
- {
- driver.client().newGet("/status.html").execute();
- var entry = waitForStatistics(statisticsCollector);
- assertEquals("http", entry.scheme);
- assertEquals("GET", entry.method);
- assertEquals("http.status.2xx", entry.name);
- assertEquals("read", entry.requestType);
- assertEquals(1, entry.value);
- }
-
- {
- handler.setRequestType(Request.RequestType.READ);
- driver.client().newPost("/status.html").execute();
- var entry = waitForStatistics(statisticsCollector);
- assertEquals("Handler overrides request type", "read", entry.requestType);
- }
-
- assertTrue(driver.close());
- }
-
- private HttpResponseStatisticsCollector.StatisticsEntry waitForStatistics(HttpResponseStatisticsCollector
- statisticsCollector) {
- List<HttpResponseStatisticsCollector.StatisticsEntry> entries = Collections.emptyList();
- int tries = 0;
- while (entries.isEmpty() && tries < 10000) {
- entries = statisticsCollector.takeStatistics();
- if (entries.isEmpty())
- try {Thread.sleep(100); } catch (InterruptedException e) {}
- tries++;
- }
- assertEquals(1, entries.size());
- return entries.get(0);
- }
-
- @Test
- public void requireThatConnectionThrottleDoesNotBlockConnectionsBelowThreshold() throws Exception {
- TestDriver driver = TestDrivers.newConfiguredInstance(
- new EchoRequestHandler(),
- new ServerConfig.Builder(),
- new ConnectorConfig.Builder()
- .throttling(new Throttling.Builder()
- .enabled(true)
- .maxAcceptRate(10)
- .maxHeapUtilization(1.0)
- .maxConnections(10)));
- driver.client().get("/status.html")
- .expectStatusCode(is(OK));
- assertTrue(driver.close());
- }
-
- @Test
- public void requireThatMetricIsIncrementedWhenClientIsMissingCertificateOnHandshake() throws IOException {
- Path privateKeyFile = tmpFolder.newFile().toPath();
- Path certificateFile = tmpFolder.newFile().toPath();
- generatePrivateKeyAndCertificate(privateKeyFile, certificateFile);
- var metricConsumer = new MetricConsumerMock();
- InMemoryConnectionLog connectionLog = new InMemoryConnectionLog();
- TestDriver driver = createSslTestDriver(certificateFile, privateKeyFile, metricConsumer, connectionLog);
-
- SSLContext clientCtx = new SslContextBuilder()
- .withTrustStore(certificateFile)
- .build();
- assertHttpsRequestTriggersSslHandshakeException(
- driver, clientCtx, null, null, "Received fatal alert: bad_certificate");
- verify(metricConsumer.mockitoMock(), atLeast(1))
- .add(MetricDefinitions.SSL_HANDSHAKE_FAILURE_MISSING_CLIENT_CERT, 1L, MetricConsumerMock.STATIC_CONTEXT);
- assertTrue(driver.close());
- Assertions.assertThat(connectionLog.logEntries()).hasSize(1);
- assertSslHandshakeFailurePresent(
- connectionLog.logEntries().get(0), SSLHandshakeException.class, SslHandshakeFailure.MISSING_CLIENT_CERT.failureType());
- }
-
- @Test
- public void requireThatMetricIsIncrementedWhenClientUsesIncompatibleTlsVersion() throws IOException {
- Path privateKeyFile = tmpFolder.newFile().toPath();
- Path certificateFile = tmpFolder.newFile().toPath();
- generatePrivateKeyAndCertificate(privateKeyFile, certificateFile);
- var metricConsumer = new MetricConsumerMock();
- InMemoryConnectionLog connectionLog = new InMemoryConnectionLog();
- TestDriver driver = createSslTestDriver(certificateFile, privateKeyFile, metricConsumer, connectionLog);
-
- SSLContext clientCtx = new SslContextBuilder()
- .withTrustStore(certificateFile)
- .withKeyStore(privateKeyFile, certificateFile)
- .build();
-
- boolean tlsv11Enabled = List.of(clientCtx.getDefaultSSLParameters().getProtocols()).contains("TLSv1.1");
- assumeTrue("TLSv1.1 must be enabled in installed JDK", tlsv11Enabled);
-
- assertHttpsRequestTriggersSslHandshakeException(driver, clientCtx, "TLSv1.1", null, "protocol");
- verify(metricConsumer.mockitoMock(), atLeast(1))
- .add(MetricDefinitions.SSL_HANDSHAKE_FAILURE_INCOMPATIBLE_PROTOCOLS, 1L, MetricConsumerMock.STATIC_CONTEXT);
- assertTrue(driver.close());
- Assertions.assertThat(connectionLog.logEntries()).hasSize(1);
- assertSslHandshakeFailurePresent(
- connectionLog.logEntries().get(0), SSLHandshakeException.class, SslHandshakeFailure.INCOMPATIBLE_PROTOCOLS.failureType());
- }
-
- @Test
- public void requireThatMetricIsIncrementedWhenClientUsesIncompatibleCiphers() throws IOException {
- Path privateKeyFile = tmpFolder.newFile().toPath();
- Path certificateFile = tmpFolder.newFile().toPath();
- generatePrivateKeyAndCertificate(privateKeyFile, certificateFile);
- var metricConsumer = new MetricConsumerMock();
- InMemoryConnectionLog connectionLog = new InMemoryConnectionLog();
- TestDriver driver = createSslTestDriver(certificateFile, privateKeyFile, metricConsumer, connectionLog);
-
- SSLContext clientCtx = new SslContextBuilder()
- .withTrustStore(certificateFile)
- .withKeyStore(privateKeyFile, certificateFile)
- .build();
-
- assertHttpsRequestTriggersSslHandshakeException(
- driver, clientCtx, null, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "Received fatal alert: handshake_failure");
- verify(metricConsumer.mockitoMock(), atLeast(1))
- .add(MetricDefinitions.SSL_HANDSHAKE_FAILURE_INCOMPATIBLE_CIPHERS, 1L, MetricConsumerMock.STATIC_CONTEXT);
- assertTrue(driver.close());
- Assertions.assertThat(connectionLog.logEntries()).hasSize(1);
- assertSslHandshakeFailurePresent(
- connectionLog.logEntries().get(0), SSLHandshakeException.class, SslHandshakeFailure.INCOMPATIBLE_CIPHERS.failureType());
- }
-
- @Test
- public void requireThatMetricIsIncrementedWhenClientUsesInvalidCertificateInHandshake() throws IOException {
- Path serverPrivateKeyFile = tmpFolder.newFile().toPath();
- Path serverCertificateFile = tmpFolder.newFile().toPath();
- generatePrivateKeyAndCertificate(serverPrivateKeyFile, serverCertificateFile);
- var metricConsumer = new MetricConsumerMock();
- InMemoryConnectionLog connectionLog = new InMemoryConnectionLog();
- TestDriver driver = createSslTestDriver(serverCertificateFile, serverPrivateKeyFile, metricConsumer, connectionLog);
-
- Path clientPrivateKeyFile = tmpFolder.newFile().toPath();
- Path clientCertificateFile = tmpFolder.newFile().toPath();
- generatePrivateKeyAndCertificate(clientPrivateKeyFile, clientCertificateFile);
-
- SSLContext clientCtx = new SslContextBuilder()
- .withKeyStore(clientPrivateKeyFile, clientCertificateFile)
- .withTrustStore(serverCertificateFile)
- .build();
-
- assertHttpsRequestTriggersSslHandshakeException(
- driver, clientCtx, null, null, "Received fatal alert: certificate_unknown");
- verify(metricConsumer.mockitoMock(), atLeast(1))
- .add(MetricDefinitions.SSL_HANDSHAKE_FAILURE_INVALID_CLIENT_CERT, 1L, MetricConsumerMock.STATIC_CONTEXT);
- assertTrue(driver.close());
- Assertions.assertThat(connectionLog.logEntries()).hasSize(1);
- assertSslHandshakeFailurePresent(
- connectionLog.logEntries().get(0), SSLHandshakeException.class, SslHandshakeFailure.INVALID_CLIENT_CERT.failureType());
- }
-
- @Test
- public void requireThatMetricIsIncrementedWhenClientUsesExpiredCertificateInHandshake() throws IOException {
- Path rootPrivateKeyFile = tmpFolder.newFile().toPath();
- Path rootCertificateFile = tmpFolder.newFile().toPath();
- Path privateKeyFile = tmpFolder.newFile().toPath();
- Path certificateFile = tmpFolder.newFile().toPath();
- Instant notAfter = Instant.now().minus(100, ChronoUnit.DAYS);
- generatePrivateKeyAndCertificate(rootPrivateKeyFile, rootCertificateFile, privateKeyFile, certificateFile, notAfter);
- var metricConsumer = new MetricConsumerMock();
- InMemoryConnectionLog connectionLog = new InMemoryConnectionLog();
- TestDriver driver = createSslTestDriver(rootCertificateFile, rootPrivateKeyFile, metricConsumer, connectionLog);
-
- SSLContext clientCtx = new SslContextBuilder()
- .withTrustStore(rootCertificateFile)
- .withKeyStore(privateKeyFile, certificateFile)
- .build();
-
- assertHttpsRequestTriggersSslHandshakeException(
- driver, clientCtx, null, null, "Received fatal alert: certificate_unknown");
- verify(metricConsumer.mockitoMock(), atLeast(1))
- .add(MetricDefinitions.SSL_HANDSHAKE_FAILURE_EXPIRED_CLIENT_CERT, 1L, MetricConsumerMock.STATIC_CONTEXT);
- assertTrue(driver.close());
- Assertions.assertThat(connectionLog.logEntries()).hasSize(1);
-
- }
-
- @Test
- public void requireThatProxyProtocolIsAcceptedAndActualRemoteAddressStoredInAccessLog() throws Exception {
- Path privateKeyFile = tmpFolder.newFile().toPath();
- Path certificateFile = tmpFolder.newFile().toPath();
- generatePrivateKeyAndCertificate(privateKeyFile, certificateFile);
- InMemoryRequestLog requestLogMock = new InMemoryRequestLog();
- InMemoryConnectionLog connectionLog = new InMemoryConnectionLog();
- TestDriver driver = createSslWithProxyProtocolTestDriver(certificateFile, privateKeyFile, requestLogMock, /*mixedMode*/connectionLog, false);
-
- String proxiedRemoteAddress = "192.168.0.100";
- int proxiedRemotePort = 12345;
- sendJettyClientRequest(driver, certificateFile, new V1.Tag(proxiedRemoteAddress, proxiedRemotePort));
- sendJettyClientRequest(driver, certificateFile, new V2.Tag(proxiedRemoteAddress, proxiedRemotePort));
- assertTrue(driver.close());
-
- assertEquals(2, requestLogMock.entries().size());
- assertLogEntryHasRemote(requestLogMock.entries().get(0), proxiedRemoteAddress, proxiedRemotePort);
- assertLogEntryHasRemote(requestLogMock.entries().get(1), proxiedRemoteAddress, proxiedRemotePort);
- Assertions.assertThat(connectionLog.logEntries()).hasSize(2);
- assertLogEntryHasRemote(connectionLog.logEntries().get(0), proxiedRemoteAddress, proxiedRemotePort);
- assertLogEntryHasRemote(connectionLog.logEntries().get(1), proxiedRemoteAddress, proxiedRemotePort);
- }
-
- @Test
- public void requireThatConnectorWithProxyProtocolMixedEnabledAcceptsBothProxyProtocolAndHttps() throws Exception {
- Path privateKeyFile = tmpFolder.newFile().toPath();
- Path certificateFile = tmpFolder.newFile().toPath();
- generatePrivateKeyAndCertificate(privateKeyFile, certificateFile);
- InMemoryRequestLog requestLogMock = new InMemoryRequestLog();
- InMemoryConnectionLog connectionLog = new InMemoryConnectionLog();
- TestDriver driver = createSslWithProxyProtocolTestDriver(certificateFile, privateKeyFile, requestLogMock, /*mixedMode*/connectionLog, true);
-
- String proxiedRemoteAddress = "192.168.0.100";
- sendJettyClientRequest(driver, certificateFile, null);
- sendJettyClientRequest(driver, certificateFile, new V2.Tag(proxiedRemoteAddress, 12345));
- assertTrue(driver.close());
-
- assertEquals(2, requestLogMock.entries().size());
- assertLogEntryHasRemote(requestLogMock.entries().get(0), "127.0.0.1", 0);
- assertLogEntryHasRemote(requestLogMock.entries().get(1), proxiedRemoteAddress, 0);
- Assertions.assertThat(connectionLog.logEntries()).hasSize(2);
- assertLogEntryHasRemote(connectionLog.logEntries().get(0), null, 0);
- assertLogEntryHasRemote(connectionLog.logEntries().get(1), proxiedRemoteAddress, 12345);
- }
-
- @Test
- public void requireThatJdiscLocalPortPropertyIsNotOverriddenByProxyProtocol() throws Exception {
- Path privateKeyFile = tmpFolder.newFile().toPath();
- Path certificateFile = tmpFolder.newFile().toPath();
- generatePrivateKeyAndCertificate(privateKeyFile, certificateFile);
- InMemoryRequestLog requestLogMock = new InMemoryRequestLog();
- InMemoryConnectionLog connectionLog = new InMemoryConnectionLog();
- TestDriver driver = createSslWithProxyProtocolTestDriver(certificateFile, privateKeyFile, requestLogMock, connectionLog, /*mixedMode*/false);
-
- String proxiedRemoteAddress = "192.168.0.100";
- int proxiedRemotePort = 12345;
- String proxyLocalAddress = "10.0.0.10";
- int proxyLocalPort = 23456;
- V2.Tag v2Tag = new V2.Tag(V2.Tag.Command.PROXY, null, V2.Tag.Protocol.STREAM,
- proxiedRemoteAddress, proxiedRemotePort, proxyLocalAddress, proxyLocalPort, null);
- ContentResponse response = sendJettyClientRequest(driver, certificateFile, v2Tag);
- assertTrue(driver.close());
-
- int clientPort = Integer.parseInt(response.getHeaders().get("Jdisc-Local-Port"));
- assertNotEquals(proxyLocalPort, clientPort);
- assertNotEquals(proxyLocalPort, connectionLog.logEntries().get(0).localPort().get().intValue());
- }
-
- @Test
- public void requireThatConnectionIsTrackedInConnectionLog() throws Exception {
- Path privateKeyFile = tmpFolder.newFile().toPath();
- Path certificateFile = tmpFolder.newFile().toPath();
- generatePrivateKeyAndCertificate(privateKeyFile, certificateFile);
- InMemoryConnectionLog connectionLog = new InMemoryConnectionLog();
- Module overrideModule = binder -> binder.bind(ConnectionLog.class).toInstance(connectionLog);
- TestDriver driver = TestDrivers.newInstanceWithSsl(new EchoRequestHandler(), certificateFile, privateKeyFile, TlsClientAuth.NEED, overrideModule);
- int listenPort = driver.server().getListenPort();
- driver.client().get("/status.html");
- assertTrue(driver.close());
- List<ConnectionLogEntry> logEntries = connectionLog.logEntries();
- Assertions.assertThat(logEntries).hasSize(1);
- ConnectionLogEntry logEntry = logEntries.get(0);
- assertEquals(4, UUID.fromString(logEntry.id()).version());
- Assertions.assertThat(logEntry.timestamp()).isAfter(Instant.EPOCH);
- Assertions.assertThat(logEntry.requests()).hasValue(1L);
- Assertions.assertThat(logEntry.responses()).hasValue(1L);
- Assertions.assertThat(logEntry.peerAddress()).hasValue("127.0.0.1");
- Assertions.assertThat(logEntry.localAddress()).hasValue("127.0.0.1");
- Assertions.assertThat(logEntry.localPort()).hasValue(listenPort);
- Assertions.assertThat(logEntry.httpBytesReceived()).hasValueSatisfying(value -> Assertions.assertThat(value).isPositive());
- Assertions.assertThat(logEntry.httpBytesSent()).hasValueSatisfying(value -> Assertions.assertThat(value).isPositive());
- Assertions.assertThat(logEntry.sslProtocol()).hasValueSatisfying(TlsContext.ALLOWED_PROTOCOLS::contains);
- Assertions.assertThat(logEntry.sslPeerSubject()).hasValue("CN=localhost");
- Assertions.assertThat(logEntry.sslCipherSuite()).hasValueSatisfying(cipher -> Assertions.assertThat(cipher).isNotBlank());
- Assertions.assertThat(logEntry.sslSessionId()).hasValueSatisfying(sessionId -> Assertions.assertThat(sessionId).hasSize(64));
- Assertions.assertThat(logEntry.sslPeerNotBefore()).hasValue(Instant.EPOCH);
- Assertions.assertThat(logEntry.sslPeerNotAfter()).hasValue(Instant.EPOCH.plus(100_000, ChronoUnit.DAYS));
- }
-
- private ContentResponse sendJettyClientRequest(TestDriver testDriver, Path certificateFile, Object tag)
- throws Exception {
- HttpClient client = createJettyHttpClient(certificateFile);
- try {
- int maxAttempts = 3;
- for (int attempt = 0; attempt < maxAttempts; attempt++) {
- try {
- ContentResponse response = client.newRequest(URI.create("https://localhost:" + testDriver.server().getListenPort() + "/"))
- .tag(tag)
- .send();
- assertEquals(200, response.getStatus());
- return response;
- } catch (ExecutionException e) {
- // Retry when the server closes the connection before the TLS handshake is completed. This have been observed in CI.
- // We have been unable to reproduce this locally. The cause is therefor currently unknown.
- log.log(Level.WARNING, String.format("Attempt %d failed: %s", attempt, e.getMessage()), e);
- Thread.sleep(10);
- }
- }
- throw new AssertionError("Failed to send request, see log for details");
- } finally {
- client.stop();
- }
- }
-
- // Using Jetty's http client as Apache httpclient does not support the proxy-protocol v1/v2.
- private static HttpClient createJettyHttpClient(Path certificateFile) throws Exception {
- SslContextFactory.Client clientSslCtxFactory = new SslContextFactory.Client();
- clientSslCtxFactory.setHostnameVerifier(NoopHostnameVerifier.INSTANCE);
- clientSslCtxFactory.setSslContext(new SslContextBuilder().withTrustStore(certificateFile).build());
-
- HttpClient client = new HttpClient(clientSslCtxFactory);
- client.start();
- return client;
- }
-
- private static void assertLogEntryHasRemote(RequestLogEntry entry, String expectedAddress, int expectedPort) {
- assertEquals(expectedAddress, entry.peerAddress().get());
- if (expectedPort > 0) {
- assertEquals(expectedPort, entry.peerPort().getAsInt());
- }
- }
-
- private static void assertLogEntryHasRemote(ConnectionLogEntry entry, String expectedAddress, int expectedPort) {
- if (expectedAddress != null) {
- Assertions.assertThat(entry.remoteAddress()).hasValue(expectedAddress);
- } else {
- Assertions.assertThat(entry.remoteAddress()).isEmpty();
- }
- if (expectedPort > 0) {
- Assertions.assertThat(entry.remotePort()).hasValue(expectedPort);
- } else {
- Assertions.assertThat(entry.remotePort()).isEmpty();
- }
- }
-
- private static void assertSslHandshakeFailurePresent(
- ConnectionLogEntry entry, Class<? extends SSLHandshakeException> expectedException, String expectedType) {
- Assertions.assertThat(entry.sslHandshakeFailure()).isPresent();
- ConnectionLogEntry.SslHandshakeFailure failure = entry.sslHandshakeFailure().get();
- assertEquals(expectedType, failure.type());
- ExceptionEntry exceptionEntry = failure.exceptionChain().get(0);
- assertEquals(expectedException.getName(), exceptionEntry.name());
- }
-
- private static TestDriver createSslWithProxyProtocolTestDriver(
- Path certificateFile, Path privateKeyFile, RequestLog requestLog,
- ConnectionLog connectionLog, boolean mixedMode) {
- ConnectorConfig.Builder connectorConfig = new ConnectorConfig.Builder()
- .proxyProtocol(new ConnectorConfig.ProxyProtocol.Builder()
- .enabled(true)
- .mixedMode(mixedMode))
- .ssl(new ConnectorConfig.Ssl.Builder()
- .enabled(true)
- .privateKeyFile(privateKeyFile.toString())
- .certificateFile(certificateFile.toString())
- .caCertificateFile(certificateFile.toString()));
- return TestDrivers.newConfiguredInstance(
- new EchoRequestHandler(),
- new ServerConfig.Builder().connectionLog(new ServerConfig.ConnectionLog.Builder().enabled(true)),
- connectorConfig,
- binder -> {
- binder.bind(RequestLog.class).toInstance(requestLog);
- binder.bind(ConnectionLog.class).toInstance(connectionLog);
- });
- }
-
- private static TestDriver createSslTestDriver(
- Path serverCertificateFile, Path serverPrivateKeyFile, MetricConsumerMock metricConsumer, InMemoryConnectionLog connectionLog) throws IOException {
- Module extraModule = binder -> {
- binder.bind(MetricConsumer.class).toInstance(metricConsumer.mockitoMock());
- binder.bind(ConnectionLog.class).toInstance(connectionLog);
- };
- return TestDrivers.newInstanceWithSsl(
- new EchoRequestHandler(), serverCertificateFile, serverPrivateKeyFile, TlsClientAuth.NEED, extraModule);
- }
-
- private static void assertHttpsRequestTriggersSslHandshakeException(
- TestDriver testDriver,
- SSLContext sslContext,
- String protocolOverride,
- String cipherOverride,
- String expectedExceptionSubstring) throws IOException {
- List<String> protocols = protocolOverride != null ? List.of(protocolOverride) : null;
- List<String> ciphers = cipherOverride != null ? List.of(cipherOverride) : null;
- try (var client = new SimpleHttpClient(sslContext, protocols, ciphers, testDriver.server().getListenPort(), false)) {
- client.get("/status.html");
- fail("SSLHandshakeException expected");
- } catch (SSLHandshakeException e) {
- assertThat(e.getMessage(), containsString(expectedExceptionSubstring));
- } catch (SSLException e) {
- // This exception is thrown if Apache httpclient's write thread detects the handshake failure before the read thread.
- log.log(Level.WARNING, "Client failed to get a proper TLS handshake response: " + e.getMessage(), e);
- // Only ignore a subset of exceptions
- assertThat(e.getMessage(), anyOf(containsString("readHandshakeRecord"), containsString("Broken pipe")));
- }
- }
-
- private static void generatePrivateKeyAndCertificate(Path privateKeyFile, Path certificateFile) throws IOException {
- KeyPair keyPair = KeyUtils.generateKeypair(EC);
- Files.writeString(privateKeyFile, KeyUtils.toPem(keyPair.getPrivate()));
-
- X509Certificate certificate = X509CertificateBuilder
- .fromKeypair(
- keyPair, new X500Principal("CN=localhost"), Instant.EPOCH, Instant.EPOCH.plus(100_000, ChronoUnit.DAYS), SHA256_WITH_ECDSA, BigInteger.ONE)
- .build();
- Files.writeString(certificateFile, X509CertificateUtils.toPem(certificate));
- }
-
- private static void generatePrivateKeyAndCertificate(Path rootPrivateKeyFile, Path rootCertificateFile,
- Path privateKeyFile, Path certificateFile, Instant notAfter) throws IOException {
- generatePrivateKeyAndCertificate(rootPrivateKeyFile, rootCertificateFile);
- X509Certificate rootCertificate = X509CertificateUtils.fromPem(Files.readString(rootCertificateFile));
- PrivateKey privateKey = KeyUtils.fromPemEncodedPrivateKey(Files.readString(rootPrivateKeyFile));
-
- KeyPair keyPair = KeyUtils.generateKeypair(EC);
- Files.writeString(privateKeyFile, KeyUtils.toPem(keyPair.getPrivate()));
- Pkcs10Csr csr = Pkcs10CsrBuilder.fromKeypair(new X500Principal("CN=myclient"), keyPair, SHA256_WITH_ECDSA).build();
- X509Certificate certificate = X509CertificateBuilder
- .fromCsr(csr, rootCertificate.getSubjectX500Principal(), Instant.EPOCH, notAfter, privateKey, SHA256_WITH_ECDSA, BigInteger.ONE)
- .build();
- Files.writeString(certificateFile, X509CertificateUtils.toPem(certificate));
- }
-
- private static RequestHandler mockRequestHandler() {
- final RequestHandler mockRequestHandler = mock(RequestHandler.class);
- when(mockRequestHandler.refer()).thenReturn(References.NOOP_REFERENCE);
- return mockRequestHandler;
- }
-
- private static String generateContent(final char c, final int len) {
- final StringBuilder ret = new StringBuilder(len);
- for (int i = 0; i < len; ++i) {
- ret.append(c);
- }
- return ret.toString();
- }
-
- private static TestDriver newDriverWithFormPostContentRemoved(RequestHandler requestHandler,
- boolean removeFormPostBody) throws Exception {
- return TestDrivers.newConfiguredInstance(
- requestHandler,
- new ServerConfig.Builder()
- .removeRawPostBodyForWwwUrlEncodedPost(removeFormPostBody),
- new ConnectorConfig.Builder());
- }
-
- private static FormBodyPart newFileBody(final String parameterName, final String fileName, final String fileContent) {
- return new FormBodyPart(
- parameterName,
- new StringBody(fileContent, ContentType.TEXT_PLAIN) {
- @Override
- public String getFilename() {
- return fileName;
- }
-
- @Override
- public String getTransferEncoding() {
- return "binary";
- }
-
- @Override
- public String getMimeType() {
- return "";
- }
-
- @Override
- public String getCharset() {
- return null;
- }
- });
- }
-
- private static class ConnectedAtRequestHandler extends AbstractRequestHandler {
-
- @Override
- public ContentChannel handleRequest(final Request request, final ResponseHandler handler) {
- final HttpRequest httpRequest = (HttpRequest)request;
- final String connectedAt = String.valueOf(httpRequest.getConnectedAt(TimeUnit.MILLISECONDS));
- final ContentChannel ch = handler.handleResponse(new Response(OK));
- ch.write(ByteBuffer.wrap(connectedAt.getBytes(StandardCharsets.UTF_8)), null);
- ch.close(null);
- return null;
- }
- }
-
- private static class CookieSetterRequestHandler extends AbstractRequestHandler {
-
- final Cookie cookie;
-
- CookieSetterRequestHandler(final Cookie cookie) {
- this.cookie = cookie;
- }
-
- @Override
- public ContentChannel handleRequest(final Request request, final ResponseHandler handler) {
- final HttpResponse response = HttpResponse.newInstance(OK);
- response.encodeSetCookieHeader(Collections.singletonList(cookie));
- ResponseDispatch.newInstance(response).dispatch(handler);
- return null;
- }
- }
-
- private static class CookiePrinterRequestHandler extends AbstractRequestHandler {
-
- @Override
- public ContentChannel handleRequest(final Request request, final ResponseHandler handler) {
- final List<Cookie> cookies = new ArrayList<>(((HttpRequest)request).decodeCookieHeader());
- Collections.sort(cookies, new CookieComparator());
- final ContentChannel out = ResponseDispatch.newInstance(Response.Status.OK).connect(handler);
- out.write(StandardCharsets.UTF_8.encode(cookies.toString()), null);
- out.close(null);
- return null;
- }
- }
-
- private static class ParameterPrinterRequestHandler extends AbstractRequestHandler {
-
- private static final CompletionHandler NULL_COMPLETION_HANDLER = null;
-
- @Override
- public ContentChannel handleRequest(Request request, ResponseHandler handler) {
- Map<String, List<String>> parameters = new TreeMap<>(((HttpRequest)request).parameters());
- ContentChannel responseContentChannel = ResponseDispatch.newInstance(Response.Status.OK).connect(handler);
- responseContentChannel.write(ByteBuffer.wrap(parameters.toString().getBytes(StandardCharsets.UTF_8)),
- NULL_COMPLETION_HANDLER);
-
- // Have the request content written back to the response.
- return responseContentChannel;
- }
- }
-
- private static class RequestTypeHandler extends AbstractRequestHandler {
-
- private Request.RequestType requestType = null;
-
- public void setRequestType(Request.RequestType requestType) {
- this.requestType = requestType;
- }
-
- @Override
- public ContentChannel handleRequest(Request request, ResponseHandler handler) {
- Response response = new Response(OK);
- response.setRequestType(requestType);
- return handler.handleResponse(response);
- }
- }
-
- private static class ThrowingHandler extends AbstractRequestHandler {
- @Override
- public ContentChannel handleRequest(final Request request, final ResponseHandler handler) {
- throw new RuntimeException("Deliberately thrown exception");
- }
- }
-
- private static class UnresponsiveHandler extends AbstractRequestHandler {
-
- ResponseHandler responseHandler;
-
- @Override
- public ContentChannel handleRequest(final Request request, final ResponseHandler handler) {
- request.setTimeout(100, TimeUnit.MILLISECONDS);
- responseHandler = handler;
- return null;
- }
- }
-
- private static class EchoRequestHandler extends AbstractRequestHandler {
-
- @Override
- public ContentChannel handleRequest(final Request request, final ResponseHandler handler) {
- int port = request.getUri().getPort();
- Response response = new Response(OK);
- response.headers().put("Jdisc-Local-Port", Integer.toString(port));
- return handler.handleResponse(response);
- }
- }
-
- private static class EchoWithHeaderRequestHandler extends AbstractRequestHandler {
-
- final String headerName;
- final String headerValue;
-
- EchoWithHeaderRequestHandler(final String headerName, final String headerValue) {
- this.headerName = headerName;
- this.headerValue = headerValue;
- }
-
- @Override
- public ContentChannel handleRequest(final Request request, final ResponseHandler handler) {
- final Response response = new Response(OK);
- response.headers().add(headerName, headerValue);
- return handler.handleResponse(response);
- }
- }
-
- private static Module newBindingSetSelector(final String setName) {
- return new AbstractModule() {
-
- @Override
- protected void configure() {
- bind(BindingSetSelector.class).toInstance(new BindingSetSelector() {
-
- @Override
- public String select(final URI uri) {
- return setName;
- }
- });
- }
- };
- }
-
- private static class CookieComparator implements Comparator<Cookie> {
-
- @Override
- public int compare(final Cookie lhs, final Cookie rhs) {
- return lhs.getName().compareTo(rhs.getName());
- }
- }
-
-}
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/InMemoryConnectionLog.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/InMemoryConnectionLog.java
deleted file mode 100644
index 6d1baf0423f..00000000000
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/InMemoryConnectionLog.java
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.jdisc.http.server.jetty;
-
-import com.yahoo.container.logging.ConnectionLog;
-import com.yahoo.container.logging.ConnectionLogEntry;
-
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-/**
- * A {@link ConnectionLog} that aggregates log entries in memory
- *
- * @author bjorncs
- */
-class InMemoryConnectionLog implements ConnectionLog {
-
- private final List<ConnectionLogEntry> logEntries = new CopyOnWriteArrayList<>();
-
- @Override
- public void log(ConnectionLogEntry entry) {
- logEntries.add(entry);
- }
-
- List<ConnectionLogEntry> logEntries() { return logEntries; }
-}
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/InMemoryRequestLog.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/InMemoryRequestLog.java
deleted file mode 100644
index b87ec5e8b8b..00000000000
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/InMemoryRequestLog.java
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.jdisc.http.server.jetty;
-
-import com.yahoo.container.logging.RequestLog;
-import com.yahoo.container.logging.RequestLogEntry;
-
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-/**
- * @author bjorncs
- */
-public class InMemoryRequestLog implements RequestLog {
-
- private final List<RequestLogEntry> entries = new CopyOnWriteArrayList<>();
-
- @Override public void log(RequestLogEntry entry) { entries.add(entry); }
-
- List<RequestLogEntry> entries() { return entries; }
-}
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/JDiscHttpServletTest.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/JDiscHttpServletTest.java
deleted file mode 100644
index 230f59cbb34..00000000000
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/JDiscHttpServletTest.java
+++ /dev/null
@@ -1,80 +0,0 @@
-// 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.server.jetty;
-
-import com.yahoo.jdisc.Request;
-import com.yahoo.jdisc.Response;
-import com.yahoo.jdisc.handler.AbstractRequestHandler;
-import com.yahoo.jdisc.handler.ContentChannel;
-import com.yahoo.jdisc.handler.RequestHandler;
-import com.yahoo.jdisc.handler.ResponseHandler;
-import org.apache.http.client.methods.HttpDelete;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpHead;
-import org.apache.http.client.methods.HttpOptions;
-import org.apache.http.client.methods.HttpPatch;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.client.methods.HttpPut;
-import org.apache.http.client.methods.HttpRequestBase;
-import org.apache.http.client.methods.HttpTrace;
-import org.junit.Test;
-
-import java.io.IOException;
-import java.net.URI;
-
-import static com.yahoo.jdisc.Response.Status.METHOD_NOT_ALLOWED;
-import static com.yahoo.jdisc.Response.Status.OK;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-/**
- * @author Simon Thoresen Hult
- */
-public class JDiscHttpServletTest {
-
- @Test
- public void requireThatServerRespondsToAllMethods() throws Exception {
- final TestDriver driver = TestDrivers.newInstance(newEchoHandler());
- final URI uri = driver.client().newUri("/status.html");
- driver.client().execute(new HttpGet(uri))
- .expectStatusCode(is(OK));
- driver.client().execute(new HttpPost(uri))
- .expectStatusCode(is(OK));
- driver.client().execute(new HttpHead(uri))
- .expectStatusCode(is(OK));
- driver.client().execute(new HttpPut(uri))
- .expectStatusCode(is(OK));
- driver.client().execute(new HttpDelete(uri))
- .expectStatusCode(is(OK));
- driver.client().execute(new HttpOptions(uri))
- .expectStatusCode(is(OK));
- driver.client().execute(new HttpTrace(uri))
- .expectStatusCode(is(OK));
- driver.client().execute(new HttpPatch(uri))
- .expectStatusCode(is(OK));
- assertThat(driver.close(), is(true));
- }
-
- @Test
- public void requireThatServerResponds405ToUnknownMethods() throws IOException {
- TestDriver driver = TestDrivers.newInstance(newEchoHandler());
- final URI uri = driver.client().newUri("/status.html");
- driver.client().execute(new UnknownMethodHttpRequest(uri))
- .expectStatusCode(is(METHOD_NOT_ALLOWED));
- assertThat(driver.close(), is(true));
- }
-
- private static RequestHandler newEchoHandler() {
- return new AbstractRequestHandler() {
-
- @Override
- public ContentChannel handleRequest(final Request request, final ResponseHandler handler) {
- return handler.handleResponse(new Response(OK));
- }
- };
- }
-
- private static class UnknownMethodHttpRequest extends HttpRequestBase {
- UnknownMethodHttpRequest(URI uri) { setURI(uri); }
- @Override public String getMethod() { return "UNKNOWN_METHOD"; }
- }
-}
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/MetricConsumerMock.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/MetricConsumerMock.java
deleted file mode 100644
index f839d83a800..00000000000
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/MetricConsumerMock.java
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.jdisc.http.server.jetty;
-
-import com.google.inject.Module;
-import com.yahoo.jdisc.Metric;
-import com.yahoo.jdisc.application.MetricConsumer;
-
-import static org.mockito.ArgumentMatchers.anyMap;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-/**
- * @author bjorncs
- */
-class MetricConsumerMock {
-
- static final Metric.Context STATIC_CONTEXT = new Metric.Context() {};
-
- private final MetricConsumer mockitoMock = mock(MetricConsumer.class);
-
- MetricConsumerMock() {
- when(mockitoMock.createContext(anyMap())).thenReturn(STATIC_CONTEXT);
- }
-
- MetricConsumer mockitoMock() { return mockitoMock; }
- Module asGuiceModule() { return binder -> binder.bind(MetricConsumer.class).toInstance(mockitoMock); }
-
-}
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/SimpleHttpClient.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/SimpleHttpClient.java
deleted file mode 100644
index f1d710bd10f..00000000000
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/SimpleHttpClient.java
+++ /dev/null
@@ -1,202 +0,0 @@
-// 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.server.jetty;
-
-import com.yahoo.jdisc.Request;
-import org.apache.http.Header;
-import org.apache.http.HttpEntity;
-import org.apache.http.HttpResponse;
-import org.apache.http.client.entity.GzipCompressingEntity;
-import org.apache.http.client.methods.CloseableHttpResponse;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.client.methods.HttpUriRequest;
-import org.apache.http.config.Registry;
-import org.apache.http.config.RegistryBuilder;
-import org.apache.http.conn.socket.ConnectionSocketFactory;
-import org.apache.http.conn.ssl.DefaultHostnameVerifier;
-import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
-import org.apache.http.entity.ByteArrayEntity;
-import org.apache.http.entity.StringEntity;
-import org.apache.http.entity.mime.FormBodyPart;
-import org.apache.http.entity.mime.MultipartEntityBuilder;
-import org.apache.http.impl.client.CloseableHttpClient;
-import org.apache.http.impl.client.HttpClientBuilder;
-import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
-import org.apache.http.util.EntityUtils;
-import org.hamcrest.Matcher;
-import org.hamcrest.MatcherAssert;
-
-import javax.net.ssl.SSLContext;
-import java.io.IOException;
-import java.net.URI;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.List;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.not;
-import static org.hamcrest.CoreMatchers.nullValue;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertNotNull;
-
-/**
- * A simple http client for testing
- *
- * @author Simon Thoresen Hult
- * @author bjorncs
- */
-public class SimpleHttpClient implements AutoCloseable {
-
- private final CloseableHttpClient delegate;
- private final String scheme;
- private final int listenPort;
-
- public SimpleHttpClient(SSLContext sslContext, int listenPort, boolean useCompression) {
- this(sslContext, null, null, listenPort, useCompression);
- }
-
- public SimpleHttpClient(SSLContext sslContext, List<String> enabledProtocols, List<String> enabledCiphers,
- int listenPort, boolean useCompression) {
- HttpClientBuilder builder = HttpClientBuilder.create();
- if (!useCompression) {
- builder.disableContentCompression();
- }
- if (sslContext != null) {
- SSLConnectionSocketFactory sslConnectionFactory = new SSLConnectionSocketFactory(
- sslContext,
- toArray(enabledProtocols),
- toArray(enabledCiphers),
- new DefaultHostnameVerifier());
- builder.setSSLSocketFactory(sslConnectionFactory);
-
- Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
- .register("https", sslConnectionFactory)
- .build();
- builder.setConnectionManager(new BasicHttpClientConnectionManager(registry));
- scheme = "https";
- } else {
- scheme = "http";
- }
- this.delegate = builder.build();
- this.listenPort = listenPort;
- }
-
- private static String[] toArray(List<String> list) {
- return list != null ? list.toArray(new String[0]) : null;
- }
-
- public URI newUri(final String path) {
- return URI.create(scheme + "://localhost:" + listenPort + path);
- }
-
- public RequestExecutor newGet(String path) {
- return newRequest(new HttpGet(newUri(path)));
- }
-
- public RequestExecutor newPost(String path) {
- return newRequest(new HttpPost(newUri(path)));
- }
-
- public RequestExecutor newRequest(HttpUriRequest request) {
- return new RequestExecutor().setRequest(request);
- }
-
- public ResponseValidator execute(HttpUriRequest request) throws IOException {
- return newRequest(request).execute();
- }
-
- public ResponseValidator get(String path) throws IOException {
- return newGet(path).execute();
- }
-
- @Override
- public void close() throws IOException {
- delegate.close();
- }
-
- public class RequestExecutor {
-
- private HttpUriRequest request;
- private HttpEntity entity;
-
- public RequestExecutor setRequest(final HttpUriRequest request) {
- this.request = request;
- return this;
- }
-
- public RequestExecutor addHeader(final String name, final String value) {
- this.request.addHeader(name, value);
- return this;
- }
-
- public RequestExecutor setContent(final String content) {
- this.entity = new StringEntity(content, StandardCharsets.UTF_8);
- return this;
- }
-
- public RequestExecutor setGzipContent(String content) {
- this.entity = new GzipCompressingEntity(new StringEntity(content, StandardCharsets.UTF_8));
- return this;
- }
-
- public RequestExecutor setBinaryContent(final byte[] content) {
- this.entity = new ByteArrayEntity(content);
- return this;
- }
-
- public RequestExecutor setMultipartContent(final FormBodyPart... parts) {
- MultipartEntityBuilder builder = MultipartEntityBuilder.create();
- Arrays.stream(parts).forEach(part -> builder.addPart(part.getName(), part.getBody()));
- this.entity = builder.build();
- return this;
- }
-
- public ResponseValidator execute() throws IOException {
- if (entity != null) {
- ((HttpPost)request).setEntity(entity);
- }
- try (CloseableHttpResponse response = delegate.execute(request)){
- return new ResponseValidator(response);
- }
- }
- }
-
- public static class ResponseValidator {
-
- private final HttpResponse response;
- private final String content;
-
- public ResponseValidator(HttpResponse response) throws IOException {
- this.response = response;
-
- HttpEntity entity = response.getEntity();
- this.content = entity == null ? null : EntityUtils.toString(entity, StandardCharsets.UTF_8);
- }
-
- public ResponseValidator expectStatusCode(Matcher<Integer> matcher) {
- MatcherAssert.assertThat(response.getStatusLine().getStatusCode(), matcher);
- return this;
- }
-
- public ResponseValidator expectHeader(String headerName, Matcher<String> matcher) {
- Header firstHeader = response.getFirstHeader(headerName);
- String headerValue = firstHeader != null ? firstHeader.getValue() : null;
- MatcherAssert.assertThat(headerValue, matcher);
- assertNotNull(firstHeader);
- return this;
- }
-
- public ResponseValidator expectNoHeader(String headerName) {
- Header firstHeader = response.getFirstHeader(headerName);
- assertThat(firstHeader, is(nullValue()));
- return this;
- }
-
- public ResponseValidator expectContent(final Matcher<String> matcher) {
- MatcherAssert.assertThat(content, matcher);
- return this;
- }
-
- }
-
-}
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/SslHandshakeFailedListenerTest.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/SslHandshakeFailedListenerTest.java
deleted file mode 100644
index 20f050d715d..00000000000
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/SslHandshakeFailedListenerTest.java
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.jdisc.http.server.jetty;
-
-import com.yahoo.jdisc.Metric;
-import org.eclipse.jetty.io.ssl.SslHandshakeListener;
-import org.junit.Test;
-
-import javax.net.ssl.SSLEngine;
-import javax.net.ssl.SSLHandshakeException;
-import java.util.Map;
-
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-/**
- * @author mortent
- */
-public class SslHandshakeFailedListenerTest {
-
- private Metric metrics = mock(Metric.class);
- SslHandshakeFailedListener listener = new SslHandshakeFailedListener(metrics, "connector", 1234);
-
- @Test
- public void includes_client_ip_dimension_present_when_peer_available() {
- listener.handshakeFailed(handshakeEvent(true), new SSLHandshakeException("Empty server certificate chain"));
- verify(metrics).createContext(eq(Map.of("clientIp", "127.0.0.1", "serverName", "connector", "serverPort", 1234)));
- }
-
- @Test
- public void does_not_include_client_ip_dimension_present_when_peer_unavailable() {
- listener.handshakeFailed(handshakeEvent(false), new SSLHandshakeException("Empty server certificate chain"));
- verify(metrics).createContext(eq(Map.of("serverName", "connector", "serverPort", 1234)));
- }
-
- private SslHandshakeListener.Event handshakeEvent(boolean includePeer) {
- var sslEngine = mock(SSLEngine.class);
- if(includePeer) when(sslEngine.getPeerHost()).thenReturn("127.0.0.1");
- return new SslHandshakeListener.Event(sslEngine);
- }
-}
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/TestDriver.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/TestDriver.java
deleted file mode 100644
index 875889ed5ce..00000000000
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/TestDriver.java
+++ /dev/null
@@ -1,79 +0,0 @@
-// 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.server.jetty;
-
-import com.google.inject.Module;
-import com.yahoo.jdisc.application.ContainerBuilder;
-import com.yahoo.jdisc.handler.RequestHandler;
-import com.yahoo.jdisc.http.ConnectorConfig;
-import com.yahoo.security.SslContextBuilder;
-
-import javax.net.ssl.SSLContext;
-import java.nio.file.Paths;
-
-import static com.yahoo.yolean.Exceptions.uncheck;
-
-/**
- * This class is based on the class by the same name in the jdisc_http_service module.
- * It provides functionality for setting up a jdisc container with an HTTP server and handlers.
- *
- * @author Simon Thoresen Hult
- * @author bakksjo
- */
-public class TestDriver {
-
- private final com.yahoo.jdisc.test.TestDriver driver;
- private final JettyHttpServer server;
- private final SimpleHttpClient client;
-
- private TestDriver(com.yahoo.jdisc.test.TestDriver driver, JettyHttpServer server, SimpleHttpClient client) {
- this.driver = driver;
- this.server = server;
- this.client = client;
- }
-
- public static TestDriver newInstance(Class<? extends JettyHttpServer> serverClass,
- RequestHandler requestHandler,
- Module testConfig) {
- com.yahoo.jdisc.test.TestDriver driver =
- com.yahoo.jdisc.test.TestDriver.newSimpleApplicationInstance(testConfig);
- ContainerBuilder builder = driver.newContainerBuilder();
- JettyHttpServer server = builder.getInstance(serverClass);
- builder.serverProviders().install(server);
- builder.serverBindings().bind("http://*/*", requestHandler);
- driver.activateContainer(builder);
- server.start();
-
- SimpleHttpClient client = new SimpleHttpClient(newSslContext(builder), server.getListenPort(), false);
- return new TestDriver(driver, server, client);
- }
-
- public boolean close() {
- server.close();
- server.release();
- uncheck(client::close);
- return driver.close();
- }
-
- public JettyHttpServer server() { return server; }
-
- public SimpleHttpClient client() { return client; }
-
- public SimpleHttpClient newClient(final boolean useCompression) {
- return new SimpleHttpClient(newSslContext(), server.getListenPort(), useCompression);
- }
-
- public SSLContext newSslContext() {
- return newSslContext(driver.newContainerBuilder());
- }
-
- private static SSLContext newSslContext(ContainerBuilder builder) {
- ConnectorConfig.Ssl sslConfig = builder.getInstance(ConnectorConfig.class).ssl();
- if (!sslConfig.enabled()) return null;
-
- return new SslContextBuilder()
- .withKeyStore(Paths.get(sslConfig.privateKeyFile()), Paths.get(sslConfig.certificateFile()))
- .withTrustStore(Paths.get(sslConfig.caCertificateFile()))
- .build();
- }
-
-}
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/TestDrivers.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/TestDrivers.java
deleted file mode 100644
index 7d7530c32e0..00000000000
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/TestDrivers.java
+++ /dev/null
@@ -1,94 +0,0 @@
-// 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.server.jetty;
-
-import com.google.inject.AbstractModule;
-import com.google.inject.Module;
-import com.google.inject.util.Modules;
-import com.yahoo.container.logging.ConnectionLog;
-import com.yahoo.container.logging.RequestLog;
-import com.yahoo.jdisc.handler.RequestHandler;
-import com.yahoo.jdisc.http.ConnectorConfig;
-import com.yahoo.jdisc.http.ServerConfig;
-import com.yahoo.jdisc.http.ServletPathsConfig;
-import com.yahoo.jdisc.http.guiceModules.ConnectorFactoryRegistryModule;
-import com.yahoo.jdisc.http.guiceModules.ServletModule;
-
-import java.nio.file.Path;
-
-/**
- * @author Simon Thoresen Hult
- * @author bjorncs
- */
-public class TestDrivers {
-
- public static TestDriver newConfiguredInstance(RequestHandler requestHandler,
- ServerConfig.Builder serverConfig,
- ConnectorConfig.Builder connectorConfig,
- Module... guiceModules) {
- return TestDriver.newInstance(
- JettyHttpServer.class,
- requestHandler,
- newConfigModule(serverConfig, connectorConfig, guiceModules));
- }
-
- public static TestDriver newInstance(RequestHandler requestHandler, Module... guiceModules) {
- return TestDriver.newInstance(
- JettyHttpServer.class,
- requestHandler,
- newConfigModule(
- new ServerConfig.Builder(),
- new ConnectorConfig.Builder(),
- guiceModules
- ));
- }
-
- public enum TlsClientAuth { NEED, WANT }
-
- public static TestDriver newInstanceWithSsl(RequestHandler requestHandler,
- Path certificateFile,
- Path privateKeyFile,
- TlsClientAuth tlsClientAuth,
- Module... guiceModules) {
- return TestDriver.newInstance(
- JettyHttpServer.class,
- requestHandler,
- newConfigModule(
- new ServerConfig.Builder().connectionLog(new ServerConfig.ConnectionLog.Builder().enabled(true)),
- new ConnectorConfig.Builder()
- .tlsClientAuthEnforcer(
- new ConnectorConfig.TlsClientAuthEnforcer.Builder()
- .enable(true)
- .pathWhitelist("/status.html"))
- .ssl(new ConnectorConfig.Ssl.Builder()
- .enabled(true)
- .clientAuth(tlsClientAuth == TlsClientAuth.NEED
- ? ConnectorConfig.Ssl.ClientAuth.Enum.NEED_AUTH
- : ConnectorConfig.Ssl.ClientAuth.Enum.WANT_AUTH)
- .privateKeyFile(privateKeyFile.toString())
- .certificateFile(certificateFile.toString())
- .caCertificateFile(certificateFile.toString())),
- guiceModules));
- }
-
- private static Module newConfigModule(ServerConfig.Builder serverConfig,
- ConnectorConfig.Builder connectorConfigBuilder,
- Module... guiceModules) {
- return Modules.override(
- Modules.combine(
- new AbstractModule() {
- @Override
- protected void configure() {
- bind(ServletPathsConfig.class).toInstance(new ServletPathsConfig(new ServletPathsConfig.Builder()));
- bind(ServerConfig.class).toInstance(new ServerConfig(serverConfig));
- bind(ConnectorConfig.class).toInstance(new ConnectorConfig(connectorConfigBuilder));
- bind(FilterBindings.class).toInstance(new FilterBindings.Builder().build());
- bind(ConnectionLog.class).toInstance(new VoidConnectionLog());
- bind(RequestLog.class).toInstance(new VoidRequestLog());
- }
- },
- new ConnectorFactoryRegistryModule(connectorConfigBuilder),
- new ServletModule()))
- .with(guiceModules);
- }
-
-}
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/servlet/JDiscFilterForServletTest.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/servlet/JDiscFilterForServletTest.java
deleted file mode 100644
index 16969a47b84..00000000000
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/servlet/JDiscFilterForServletTest.java
+++ /dev/null
@@ -1,166 +0,0 @@
-// 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.server.jetty.servlet;
-
-import com.google.inject.AbstractModule;
-import com.google.inject.Module;
-import com.google.inject.util.Modules;
-import com.yahoo.jdisc.AbstractResource;
-import com.yahoo.jdisc.Request;
-import com.yahoo.jdisc.Response;
-import com.yahoo.jdisc.handler.ContentChannel;
-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 com.yahoo.jdisc.http.server.jetty.FilterBindings;
-import com.yahoo.jdisc.http.server.jetty.FilterInvoker;
-import com.yahoo.jdisc.http.server.jetty.SimpleHttpClient.ResponseValidator;
-import com.yahoo.jdisc.http.server.jetty.TestDriver;
-import com.yahoo.jdisc.http.server.jetty.TestDrivers;
-import org.junit.Test;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.net.URI;
-import java.nio.ByteBuffer;
-import java.nio.charset.StandardCharsets;
-
-import static org.hamcrest.CoreMatchers.containsString;
-import static org.hamcrest.CoreMatchers.is;
-
-/**
- * @author Tony Vaagenes
- * @author bjorncs
- */
-public class JDiscFilterForServletTest extends ServletTestBase {
- @Test
- public void request_filter_can_return_response() throws IOException, InterruptedException {
- TestDriver testDriver = requestFilterTestDriver();
- ResponseValidator response = httpGet(testDriver, TestServlet.PATH).execute();
-
- response.expectContent(containsString(TestRequestFilter.responseContent));
- }
-
- @Test
- public void request_can_be_forwarded_through_request_filter_to_servlet() throws IOException {
- TestDriver testDriver = requestFilterTestDriver();
- ResponseValidator response = httpGet(testDriver, TestServlet.PATH).
- addHeader(TestRequestFilter.BYPASS_FILTER_HEADER, Boolean.TRUE.toString()).
- execute();
-
- response.expectContent(containsString(TestServlet.RESPONSE_CONTENT));
- }
-
- @Test
- public void response_filter_can_modify_response() throws IOException {
- TestDriver testDriver = responseFilterTestDriver();
- ResponseValidator response = httpGet(testDriver, TestServlet.PATH).execute();
-
- response.expectHeader(TestResponseFilter.INVOKED_HEADER, is(Boolean.TRUE.toString()));
- }
-
- @Test
- public void response_filter_is_run_on_empty_sync_response() throws IOException {
- TestDriver testDriver = responseFilterTestDriver();
- ResponseValidator response = httpGet(testDriver, NoContentTestServlet.PATH).execute();
-
- response.expectHeader(TestResponseFilter.INVOKED_HEADER, is(Boolean.TRUE.toString()));
- }
-
- @Test
- public void response_filter_is_run_on_empty_async_response() throws IOException {
- TestDriver testDriver = responseFilterTestDriver();
- ResponseValidator response = httpGet(testDriver, NoContentTestServlet.PATH).
- addHeader(NoContentTestServlet.HEADER_ASYNC, Boolean.TRUE.toString()).
- execute();
-
- response.expectHeader(TestResponseFilter.INVOKED_HEADER, is(Boolean.TRUE.toString()));
- }
-
- private TestDriver requestFilterTestDriver() throws IOException {
- FilterBindings filterBindings = new FilterBindings.Builder()
- .addRequestFilter("my-request-filter", new TestRequestFilter())
- .addRequestFilterBinding("my-request-filter", "http://*/*")
- .build();
- return TestDrivers.newInstance(dummyRequestHandler, bindings(filterBindings));
- }
-
- private TestDriver responseFilterTestDriver() throws IOException {
- FilterBindings filterBindings = new FilterBindings.Builder()
- .addResponseFilter("my-response-filter", new TestResponseFilter())
- .addResponseFilterBinding("my-response-filter", "http://*/*")
- .build();
- return TestDrivers.newInstance(dummyRequestHandler, bindings(filterBindings));
- }
-
-
-
- private Module bindings(FilterBindings filterBindings) {
- return Modules.combine(
- new AbstractModule() {
- @Override
- protected void configure() {
- bind(FilterBindings.class).toInstance(filterBindings);
- bind(FilterInvoker.class).toInstance(new FilterInvoker() {
- @Override
- public HttpServletRequest invokeRequestFilterChain(
- RequestFilter requestFilter,
- URI uri,
- HttpServletRequest httpRequest,
- ResponseHandler responseHandler) {
- TestRequestFilter filter = (TestRequestFilter) requestFilter;
- filter.runAsSecurityFilter(httpRequest, responseHandler);
- return httpRequest;
- }
-
- @Override
- public void invokeResponseFilterChain(
- ResponseFilter responseFilter,
- URI uri,
- HttpServletRequest request,
- HttpServletResponse response) {
-
- TestResponseFilter filter = (TestResponseFilter) responseFilter;
- filter.runAsSecurityFilter(request, response);
- }
- });
- }
- },
- guiceModule());
- }
-
- static class TestRequestFilter extends AbstractResource implements RequestFilter {
- static final String simpleName = TestRequestFilter.class.getSimpleName();
- static final String responseContent = "Rejected by " + simpleName;
- static final String BYPASS_FILTER_HEADER = "BYPASS_HEADER" + simpleName;
-
- @Override
- public void filter(HttpRequest request, ResponseHandler handler) {
- throw new UnsupportedOperationException();
- }
-
- public void runAsSecurityFilter(HttpServletRequest request, ResponseHandler responseHandler) {
- if (Boolean.parseBoolean(request.getHeader(BYPASS_FILTER_HEADER)))
- return;
-
- ContentChannel contentChannel = responseHandler.handleResponse(new Response(500));
- contentChannel.write(ByteBuffer.wrap(responseContent.getBytes(StandardCharsets.UTF_8)), null);
- contentChannel.close(null);
- }
- }
-
-
- static class TestResponseFilter extends AbstractResource implements ResponseFilter {
- static final String INVOKED_HEADER = TestResponseFilter.class.getSimpleName() + "_INVOKED_HEADER";
-
- @Override
- public void filter(Response response, Request request) {
- throw new UnsupportedClassVersionError();
- }
-
- public void runAsSecurityFilter(HttpServletRequest request, HttpServletResponse response) {
- response.addHeader(INVOKED_HEADER, Boolean.TRUE.toString());
- }
- }
-}
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/servlet/ServletAccessLoggingTest.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/servlet/ServletAccessLoggingTest.java
deleted file mode 100644
index a533a447f6a..00000000000
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/servlet/ServletAccessLoggingTest.java
+++ /dev/null
@@ -1,64 +0,0 @@
-// 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.server.jetty.servlet;
-
-import com.google.inject.AbstractModule;
-import com.google.inject.Module;
-import com.google.inject.util.Modules;
-import com.yahoo.container.logging.AccessLog;
-import com.yahoo.container.logging.RequestLog;
-import com.yahoo.container.logging.RequestLogEntry;
-import com.yahoo.jdisc.http.server.jetty.TestDriver;
-import com.yahoo.jdisc.http.server.jetty.TestDrivers;
-import org.junit.Test;
-import org.mockito.verification.VerificationMode;
-
-import java.io.IOException;
-import java.util.concurrent.TimeUnit;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.verify;
-
-/**
- * @author bakksjo
- * @author bjorncs
- */
-public class ServletAccessLoggingTest extends ServletTestBase {
- private static final long MAX_LOG_WAIT_TIME_MILLIS = TimeUnit.SECONDS.toMillis(60);
-
- @Test
- public void accessLogIsInvokedForNonJDiscServlet() throws Exception {
- final AccessLog accessLog = mock(AccessLog.class);
- final TestDriver testDriver = newTestDriver(accessLog);
- httpGet(testDriver, TestServlet.PATH).execute();
- verifyCallsLog(accessLog, timeout(MAX_LOG_WAIT_TIME_MILLIS).times(1));
- }
-
- @Test
- public void accessLogIsInvokedForJDiscServlet() throws Exception {
- final AccessLog accessLog = mock(AccessLog.class);
- final TestDriver testDriver = newTestDriver(accessLog);
- testDriver.client().newGet("/status.html").execute();
- verifyCallsLog(accessLog, timeout(MAX_LOG_WAIT_TIME_MILLIS).times(1));
- }
-
- private void verifyCallsLog(RequestLog requestLog, final VerificationMode verificationMode) {
- verify(requestLog, verificationMode).log(any(RequestLogEntry.class));
- }
-
- private TestDriver newTestDriver(RequestLog requestLog) throws IOException {
- return TestDrivers.newInstance(dummyRequestHandler, bindings(requestLog));
- }
-
- private Module bindings(RequestLog requestLog) {
- return Modules.combine(
- new AbstractModule() {
- @Override
- protected void configure() {
- bind(RequestLog.class).toInstance(requestLog);
- }
- },
- guiceModule());
- }
-}
diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/servlet/ServletTestBase.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/servlet/ServletTestBase.java
deleted file mode 100644
index 54bfe8c026d..00000000000
--- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/servlet/ServletTestBase.java
+++ /dev/null
@@ -1,132 +0,0 @@
-// 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.server.jetty.servlet;
-
-import com.google.inject.AbstractModule;
-import com.google.inject.Module;
-import com.google.inject.TypeLiteral;
-import com.yahoo.component.ComponentId;
-import com.yahoo.component.provider.ComponentRegistry;
-import com.yahoo.jdisc.Request;
-import com.yahoo.jdisc.handler.AbstractRequestHandler;
-import com.yahoo.jdisc.handler.ContentChannel;
-import com.yahoo.jdisc.handler.RequestHandler;
-import com.yahoo.jdisc.handler.ResponseHandler;
-import com.yahoo.jdisc.http.ServletPathsConfig;
-import com.yahoo.jdisc.http.ServletPathsConfig.Servlets.Builder;
-import com.yahoo.jdisc.http.server.jetty.SimpleHttpClient.RequestExecutor;
-import com.yahoo.jdisc.http.server.jetty.TestDriver;
-import org.eclipse.jetty.servlet.ServletHolder;
-
-import javax.servlet.ServletException;
-import javax.servlet.annotation.WebServlet;
-import javax.servlet.http.HttpServlet;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.List;
-
-/**
- * @author Tony Vaagenes
- * @author bakksjo
- */
-public class ServletTestBase {
-
- private static class ServletInstance {
- final ComponentId componentId; final String path; final HttpServlet instance;
-
- ServletInstance(ComponentId componentId, String path, HttpServlet instance) {
- this.componentId = componentId;
- this.path = path;
- this.instance = instance;
- }
- }
-
- private final List<ServletInstance> servlets = List.of(
- new ServletInstance(TestServlet.ID, TestServlet.PATH, new TestServlet()),
- new ServletInstance(NoContentTestServlet.ID, NoContentTestServlet.PATH, new NoContentTestServlet()));
-
- protected RequestExecutor httpGet(TestDriver testDriver, String path) {
- return testDriver.client().newGet("/" + path);
- }
-
- protected ServletPathsConfig createServletPathConfig() {
- ServletPathsConfig.Builder configBuilder = new ServletPathsConfig.Builder();
-
- servlets.forEach(servlet ->
- configBuilder.servlets(
- servlet.componentId.stringValue(),
- new Builder().path(servlet.path)));
-
- return new ServletPathsConfig(configBuilder);
- }
-
- protected ComponentRegistry<ServletHolder> servlets() {
- ComponentRegistry<ServletHolder> result = new ComponentRegistry<>();
-
- servlets.forEach(servlet ->
- result.register(servlet.componentId, new ServletHolder(servlet.instance)));
-
- result.freeze();
- return result;
- }
-
- protected Module guiceModule() {
- return new AbstractModule() {
- @Override
- protected void configure() {
- bind(new TypeLiteral<ComponentRegistry<ServletHolder>>(){}).toInstance(servlets());
- bind(ServletPathsConfig.class).toInstance(createServletPathConfig());
- }
- };
- }
-
- protected static class TestServlet extends HttpServlet {
- static final String PATH = "servlet/test-servlet";
- static final ComponentId ID = ComponentId.fromString("test-servlet");
- static final String RESPONSE_CONTENT = "Response from " + TestServlet.class.getSimpleName();
-
- @Override
- protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
- response.setContentType("text/plain");
- PrintWriter writer = response.getWriter();
- writer.write(RESPONSE_CONTENT);
- writer.close();
- }
- }
-
- @WebServlet(asyncSupported = true)
- protected static class NoContentTestServlet extends HttpServlet {
- static final String HEADER_ASYNC = "HEADER_ASYNC";
-
- static final String PATH = "servlet/no-content-test-servlet";
- static final ComponentId ID = ComponentId.fromString("no-content-test-servlet");
-
- @Override
- protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
- if (request.getHeader(HEADER_ASYNC) != null) {
- asyncGet(request);
- }
- }
-
- private void asyncGet(HttpServletRequest request) {
- request.startAsync().start(() -> {
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- log("Interrupted", e);
- } finally {
- request.getAsyncContext().complete();
- }
- });
- }
- }
-
-
- protected static final RequestHandler dummyRequestHandler = new AbstractRequestHandler() {
- @Override
- public ContentChannel handleRequest(Request request, ResponseHandler handler) {
- throw new UnsupportedOperationException();
- }
- };
-}