summaryrefslogtreecommitdiffstats
path: root/container-core/src/test/java
diff options
context:
space:
mode:
authorBjørn Christian Seime <bjorncs@verizonmedia.com>2021-04-08 16:11:57 +0200
committerBjørn Christian Seime <bjorncs@verizonmedia.com>2021-04-12 13:43:38 +0200
commitbfaf3f88b7b0a8fd094aef101e920709ed788731 (patch)
tree969df326c9222eecdd6e25754ec27766e947c07d /container-core/src/test/java
parent791c4b163669d5ef8ea671be1efacb89655d3935 (diff)
Add HTTP/2 support to jdisc
Diffstat (limited to 'container-core/src/test/java')
-rw-r--r--container-core/src/test/java/com/yahoo/jdisc/http/filter/ServletFilterRequestTest.java27
-rw-r--r--container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/AccessLogRequestLogTest.java89
-rw-r--r--container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/FilterTestCase.java2
-rw-r--r--container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpRequestFactoryTest.java30
-rw-r--r--container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerTest.java108
-rw-r--r--container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/JDiscHttpServletTest.java23
-rw-r--r--container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/JettyMockRequestBuilder.java176
-rw-r--r--container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/JettyMockResponseBuilder.java29
-rw-r--r--container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/SimpleHttpClient.java83
-rw-r--r--container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/TestDrivers.java5
10 files changed, 393 insertions, 179 deletions
diff --git a/container-core/src/test/java/com/yahoo/jdisc/http/filter/ServletFilterRequestTest.java b/container-core/src/test/java/com/yahoo/jdisc/http/filter/ServletFilterRequestTest.java
index 3052902f174..ed4c9b66068 100644
--- a/container-core/src/test/java/com/yahoo/jdisc/http/filter/ServletFilterRequestTest.java
+++ b/container-core/src/test/java/com/yahoo/jdisc/http/filter/ServletFilterRequestTest.java
@@ -3,12 +3,11 @@ package com.yahoo.jdisc.http.filter;
import com.yahoo.jdisc.http.Cookie;
import com.yahoo.jdisc.http.HttpHeaders;
+import com.yahoo.jdisc.http.server.jetty.JettyMockRequestBuilder;
import com.yahoo.jdisc.http.servlet.ServletRequest;
-import org.eclipse.jetty.server.HttpConnection;
+import org.eclipse.jetty.server.Request;
import org.junit.Before;
import org.junit.Test;
-import org.mockito.Mockito;
-import org.springframework.mock.web.MockHttpServletRequest;
import java.net.URI;
import java.util.Arrays;
@@ -18,7 +17,6 @@ import java.util.List;
import static com.yahoo.jdisc.http.HttpRequest.Version;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.when;
/**
* Test the parts of the DiscFilterRequest API that are implemented
@@ -26,7 +24,6 @@ import static org.mockito.Mockito.when;
* {@link com.yahoo.jdisc.http.servlet.ServletRequest}.
*
* @author gjoranv
- * @since 5.27
*/
public class ServletFilterRequestTest {
@@ -54,18 +51,14 @@ public class ServletFilterRequestTest {
parentRequest = ((ServletFilterRequest)filterRequest).getServletRequest();
}
- private ServletRequest newServletRequest() throws Exception {
- MockHttpServletRequest parent = new MockHttpServletRequest("GET", uri.toString());
- parent.setProtocol(Version.HTTP_1_1.toString());
- parent.setRemoteHost(host);
- parent.setRemotePort(port);
- parent.setParameter(paramName, paramValue);
- parent.setParameter(listParamName, listParamValue);
- parent.addHeader(headerName, headerValue);
- parent.setAttribute(attributeName, attributeValue);
- HttpConnection connection = Mockito.mock(HttpConnection.class);
- when(connection.getCreatedTimeStamp()).thenReturn(System.currentTimeMillis());
- parent.setAttribute("org.eclipse.jetty.server.HttpConnection", connection);
+ private ServletRequest newServletRequest() {
+ Request parent = JettyMockRequestBuilder.newBuilder()
+ .remote("1.2.3.4", host, port)
+ .header(headerName, List.of(headerValue))
+ .parameter(paramName, List.of(paramValue))
+ .parameter(listParamName, List.of(listParamValue))
+ .attribute(attributeName, attributeValue)
+ .build();
return new ServletRequest(parent, uri);
}
diff --git a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/AccessLogRequestLogTest.java b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/AccessLogRequestLogTest.java
index e472f954afc..c45d17a4ff8 100644
--- a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/AccessLogRequestLogTest.java
+++ b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/AccessLogRequestLogTest.java
@@ -4,12 +4,7 @@ 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.HttpInput;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.junit.Test;
@@ -23,8 +18,6 @@ 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
@@ -33,9 +26,9 @@ import static org.mockito.Mockito.when;
public class AccessLogRequestLogTest {
@Test
public void requireThatQueryWithUnquotedSpecialCharactersIsHandled() {
- final Request jettyRequest = createRequestMock();
- when(jettyRequest.getRequestURI()).thenReturn("/search/");
- when(jettyRequest.getQueryString()).thenReturn("query=year:>2010");
+ Request jettyRequest = createRequestBuilder()
+ .uri("http", "localhost", 12345, "/search/", "query=year:>2010")
+ .build();
InMemoryRequestLog requestLog = new InMemoryRequestLog();
doAccessLoggingOfRequest(requestLog, jettyRequest);
@@ -47,11 +40,11 @@ public class AccessLogRequestLogTest {
@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);
+ String path = "/search/";
+ String query = "query=year%252010+%3B&customParameter=something";
+ Request jettyRequest = createRequestBuilder()
+ .uri("http", "localhost", 12345, path, query)
+ .build();
InMemoryRequestLog requestLog = new InMemoryRequestLog();
doAccessLoggingOfRequest(requestLog, jettyRequest);
@@ -64,11 +57,11 @@ public class AccessLogRequestLogTest {
@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);
+ Request jettyRequest = createRequestBuilder()
+ .uri("http", "localhost", 12345, rawPath, rawQuery)
+ .build();
InMemoryRequestLog requestLog = new InMemoryRequestLog();
doAccessLoggingOfRequest(requestLog, jettyRequest);
@@ -81,11 +74,11 @@ public class AccessLogRequestLogTest {
@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");
+ Request jettyRequest = createRequestBuilder()
+ .uri("http", "localhost", 12345, "//search/", "q=%%2")
+ .header("x-forwarded-for", List.of("1.2.3.4"))
+ .header("y-ra", List.of("2.3.4.5"))
+ .build();
InMemoryRequestLog requestLog = new InMemoryRequestLog();
doAccessLoggingOfRequest(requestLog, jettyRequest);
@@ -95,11 +88,11 @@ public class AccessLogRequestLogTest {
@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");
+ Request jettyRequest = createRequestBuilder()
+ .uri("http", "localhost", 12345, "//search/", "q=%%2")
+ .header("X-Forwarded-Port", List.of("80"))
+ .header("y-rp", List.of("8080"))
+ .build();
InMemoryRequestLog requestLog = new InMemoryRequestLog();
doAccessLoggingOfRequest(requestLog, jettyRequest);
@@ -109,10 +102,12 @@ public class AccessLogRequestLogTest {
@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);
+ Request jettyRequest = createRequestBuilder()
+ .uri("http", "localhost", 12345, "/search/", null)
+ .header("X-Forwarded-Port", List.of("8o8o"))
+ .header("y-rp", List.of("8o8o"))
+ .remote("2.3.4.5", "localhost", 80)
+ .build();
InMemoryRequestLog requestLog = new InMemoryRequestLog();
doAccessLoggingOfRequest(requestLog, jettyRequest);
@@ -129,32 +124,14 @@ public class AccessLogRequestLogTest {
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);
- HttpInput httpInput = mock(HttpInput.class);
- when(httpInput.getContentReceived()).thenReturn(2345L);
- when(request.getHttpInput()).thenReturn(httpInput);
- return request;
+ private static JettyMockRequestBuilder createRequestBuilder() {
+ return JettyMockRequestBuilder.newBuilder()
+ .attribute(JDiscHttpServlet.ATTRIBUTE_NAME_ACCESS_LOG_ENTRY, new AccessLogEntry())
+ .remote("2.3.4.5", "localhost", 12345)
+ .localPort(1234);
}
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;
+ return JettyMockResponseBuilder.newBuilder().build();
}
}
diff --git a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/FilterTestCase.java b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/FilterTestCase.java
index a67656dd5ca..e117ef7f723 100644
--- a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/FilterTestCase.java
+++ b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/FilterTestCase.java
@@ -99,7 +99,7 @@ public class FilterTestCase {
final MyRequestHandler requestHandler = new MyRequestHandler();
final TestDriver testDriver = newDriver(requestHandler, filterBindings);
- testDriver.client().get("status.html");
+ testDriver.client().get("/status.html");
assertThat(requestHandler.awaitInvocation(), is(true));
assertThat(requestHandler.getHeaderMap().get("foo").get(0), is("bar"));
diff --git a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpRequestFactoryTest.java b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpRequestFactoryTest.java
index 9c1348004ee..fbbf3074839 100644
--- a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpRequestFactoryTest.java
+++ b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpRequestFactoryTest.java
@@ -7,10 +7,8 @@ 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;
@@ -22,8 +20,6 @@ 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
@@ -141,27 +137,15 @@ public class HttpRequestFactoryTest {
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 HttpServletRequest createMockRequest(String scheme, String host, String path, String query) {
+ return JettyMockRequestBuilder.newBuilder()
+ .uri(scheme, host, LOCAL_PORT, path, query)
+ .remote("127.0.0.1", "localhost", 1234)
+ .localPort(LOCAL_PORT)
+ .build();
}
+
private static final class MockContainer implements CurrentContainer {
@Override
diff --git a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerTest.java b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerTest.java
index d8e94d13813..f5d77b53f12 100644
--- a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerTest.java
+++ b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/HttpServerTest.java
@@ -35,10 +35,19 @@ 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.apache.hc.client5.http.async.methods.SimpleHttpRequests;
+import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
+import org.apache.hc.client5.http.entity.mime.FormBodyPart;
+import org.apache.hc.client5.http.entity.mime.FormBodyPartBuilder;
+import org.apache.hc.client5.http.entity.mime.StringBody;
+import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
+import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
+import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
+import org.apache.hc.client5.http.ssl.ClientTlsStrategyBuilder;
+import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
+import org.apache.hc.core5.http2.HttpVersionPolicy;
import org.assertj.core.api.Assertions;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.ProxyProtocolClientConnectionFactory.V1;
@@ -107,6 +116,7 @@ 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.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
@@ -400,8 +410,8 @@ public class HttpServerTest {
final ResponseValidator response =
driver.client().newPost("/status.html")
.setMultipartContent(
- newFileBody("", "start.txt", startTxtContent),
- newFileBody("", "updater.conf", updaterConfContent))
+ newFileBody("start.txt", startTxtContent),
+ newFileBody("updater.conf", updaterConfContent))
.execute();
response.expectStatusCode(is(OK))
.expectContent(containsString(startTxtContent))
@@ -505,11 +515,27 @@ public class HttpServerTest {
}
@Test
- public void requireThatTlsClientAuthenticationEnforcerRejectsRequestsForNonWhitelistedPaths() throws IOException {
+ public void requireThatServerCanRespondToHttp2Request() throws Exception {
Path privateKeyFile = tmpFolder.newFile().toPath();
Path certificateFile = tmpFolder.newFile().toPath();
generatePrivateKeyAndCertificate(privateKeyFile, certificateFile);
+
TestDriver driver = TestDrivers.newInstanceWithSsl(new EchoRequestHandler(), certificateFile, privateKeyFile, TlsClientAuth.WANT);
+ try (CloseableHttpAsyncClient client = createHttp2Client(certificateFile, privateKeyFile)) {
+ String uri = "https://localhost:" + driver.server().getListenPort() + "/status.html";
+ SimpleHttpResponse response = client.execute(SimpleHttpRequests.get(uri), null).get();
+ assertNull(response.getBodyText());
+ assertEquals(OK, response.getCode());
+ }
+ assertTrue(driver.close());
+ }
+
+ @Test
+ public void requireThatTlsClientAuthenticationEnforcerRejectsRequestsForNonWhitelistedPaths() throws IOException {
+ Path privateKeyFile = tmpFolder.newFile().toPath();
+ Path certificateFile = tmpFolder.newFile().toPath();
+ generatePrivateKeyAndCertificate(privateKeyFile, certificateFile);
+ TestDriver driver = createSslWithTlsClientAuthenticationEnforcer(certificateFile, privateKeyFile);
SSLContext trustStoreOnlyCtx = new SslContextBuilder()
.withTrustStore(certificateFile)
@@ -918,6 +944,21 @@ public class HttpServerTest {
return client;
}
+ private static CloseableHttpAsyncClient createHttp2Client(Path certificateFile, Path privateKeyFile) {
+ TestDriver driver = TestDrivers.newInstanceWithSsl(new EchoRequestHandler(), certificateFile, privateKeyFile, TlsClientAuth.WANT);
+ TlsStrategy tlsStrategy = ClientTlsStrategyBuilder.create()
+ .setSslContext(driver.newSslContext())
+ .build();
+ var client = HttpAsyncClientBuilder.create()
+ .setVersionPolicy(HttpVersionPolicy.FORCE_HTTP_2)
+ .disableConnectionState()
+ .disableAutomaticRetries()
+ .setConnectionManager(PoolingAsyncClientConnectionManagerBuilder.create().setTlsStrategy(tlsStrategy).build())
+ .build();
+ client.start();
+ return client;
+ }
+
private static void assertLogEntryHasRemote(RequestLogEntry entry, String expectedAddress, int expectedPort) {
assertEquals(expectedAddress, entry.peerAddress().get());
if (expectedPort > 0) {
@@ -969,6 +1010,25 @@ public class HttpServerTest {
});
}
+ private static TestDriver createSslWithTlsClientAuthenticationEnforcer(Path certificateFile, Path privateKeyFile) {
+ ConnectorConfig.Builder connectorConfig = new ConnectorConfig.Builder()
+ .tlsClientAuthEnforcer(
+ new ConnectorConfig.TlsClientAuthEnforcer.Builder()
+ .enable(true)
+ .pathWhitelist("/status.html"))
+ .ssl(new ConnectorConfig.Ssl.Builder()
+ .enabled(true)
+ .clientAuth(ConnectorConfig.Ssl.ClientAuth.Enum.WANT_AUTH)
+ .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 -> {});
+ }
+
private static TestDriver createSslTestDriver(
Path serverCertificateFile, Path serverPrivateKeyFile, MetricConsumerMock metricConsumer, InMemoryConnectionLog connectionLog) throws IOException {
Module extraModule = binder -> {
@@ -1049,30 +1109,16 @@ public class HttpServerTest {
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 FormBodyPart newFileBody(final String fileName, final String fileContent) {
+ return FormBodyPartBuilder.create()
+ .setBody(
+ new StringBody(fileContent, ContentType.TEXT_PLAIN) {
+ @Override public String getFilename() { return fileName; }
+ @Override public String getMimeType() { return ""; }
+ @Override public String getCharset() { return null; }
+ })
+ .setName(fileName)
+ .build();
}
private static class ConnectedAtRequestHandler extends AbstractRequestHandler {
diff --git a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/JDiscHttpServletTest.java b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/JDiscHttpServletTest.java
index 230f59cbb34..23c229e2ec5 100644
--- a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/JDiscHttpServletTest.java
+++ b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/JDiscHttpServletTest.java
@@ -7,15 +7,15 @@ 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.apache.hc.client5.http.classic.methods.HttpDelete;
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.classic.methods.HttpHead;
+import org.apache.hc.client5.http.classic.methods.HttpOptions;
+import org.apache.hc.client5.http.classic.methods.HttpPatch;
+import org.apache.hc.client5.http.classic.methods.HttpPost;
+import org.apache.hc.client5.http.classic.methods.HttpPut;
+import org.apache.hc.client5.http.classic.methods.HttpTrace;
+import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
import org.junit.Test;
import java.io.IOException;
@@ -73,8 +73,7 @@ public class JDiscHttpServletTest {
};
}
- private static class UnknownMethodHttpRequest extends HttpRequestBase {
- UnknownMethodHttpRequest(URI uri) { setURI(uri); }
- @Override public String getMethod() { return "UNKNOWN_METHOD"; }
+ private static class UnknownMethodHttpRequest extends HttpUriRequestBase {
+ UnknownMethodHttpRequest(URI uri) { super("UNKNOWN_METHOD", uri); }
}
}
diff --git a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/JettyMockRequestBuilder.java b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/JettyMockRequestBuilder.java
new file mode 100644
index 00000000000..4bf6afeb3f1
--- /dev/null
+++ b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/JettyMockRequestBuilder.java
@@ -0,0 +1,176 @@
+// 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.http.ConnectorConfig;
+import org.eclipse.jetty.server.HttpChannel;
+import org.eclipse.jetty.server.HttpConnection;
+import org.eclipse.jetty.server.HttpInput;
+import org.eclipse.jetty.server.Request;
+import org.mockito.stubbing.Answer;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Builder for creating a mock instance of Jetty's {@link Request} type.
+ *
+ * @author bjorncs
+ */
+public class JettyMockRequestBuilder {
+
+ private final Map<String, List<String>> parameters = new HashMap<>();
+ private final Map<String, List<String>> headers = new HashMap<>();
+ private final Map<String, Object> attributes = new HashMap<>();
+ private Integer localPort;
+ private String uriScheme;
+ private String uriServerName;
+ private Integer uriPort;
+ private String uriPath;
+ private String uriQuery;
+ private String remoteAddress;
+ private String remoteHost;
+ private Integer remotePort;
+
+ private JettyMockRequestBuilder() {}
+
+ public static JettyMockRequestBuilder newBuilder() { return new JettyMockRequestBuilder(); }
+
+ public JettyMockRequestBuilder localPort(int localPort) { this.localPort = localPort; return this; }
+
+ public JettyMockRequestBuilder remote(String address, String host, int port) {
+ this.remoteAddress = address;
+ this.remoteHost = host;
+ this.remotePort = port;
+ return this;
+ }
+
+ public JettyMockRequestBuilder uri(String scheme, String serverName, int port, String path, String query) {
+ this.uriScheme = scheme;
+ this.uriServerName = serverName;
+ this.uriPort = port;
+ this.uriPath = path;
+ this.uriQuery = query;
+ return this;
+ }
+
+ public JettyMockRequestBuilder parameter(String name, List<String> values) { this.parameters.put(name, List.copyOf(values)); return this; }
+
+ public JettyMockRequestBuilder header(String name, List<String> values) { this.headers.put(name, List.copyOf(values)); return this; }
+
+ public JettyMockRequestBuilder attribute(String name, Object value) { this.attributes.put(name, value); return this; }
+
+ public Request build() {
+ int localPort = this.localPort != null ? this.localPort : 8080;
+ String scheme = this.uriScheme != null ? this.uriScheme : "http";
+ String serverName = this.uriServerName != null ? this.uriServerName : "localhost";
+ int uriPort = this.uriPort != null ? this.uriPort : 8080;
+ String path = this.uriPath;
+ String query = this.uriQuery;
+ String remoteAddress = this.remoteAddress != null ? this.remoteAddress : "1.2.3.4";
+ String remoteHost = this.remoteHost != null ? this.remoteHost : "remotehost";
+ Integer remotePort = this.remotePort != null ? this.remotePort : 12345;
+
+ HttpChannel channel = mock(HttpChannel.class);
+ HttpConnection connection = mock(HttpConnection.class);
+ JDiscServerConnector connector = mock(JDiscServerConnector.class);
+ when(connector.connectorConfig()).thenReturn(new ConnectorConfig(new ConnectorConfig.Builder().listenPort(localPort)));
+ when(connector.getLocalPort()).thenReturn(localPort);
+ when(connection.getCreatedTimeStamp()).thenReturn(System.currentTimeMillis());
+ when(connection.getConnector()).thenReturn(connector);
+ when(connection.getHttpChannel()).thenReturn(channel);
+ when(channel.getConnector()).thenReturn(connector);
+ when(channel.getConnection()).thenReturn(connection);
+
+ HttpInput httpInput = mock(HttpInput.class);
+ when(httpInput.getContentReceived()).thenReturn(2345L);
+
+ Request request = mock(Request.class);
+ when(request.getHttpChannel()).thenReturn(channel);
+ when(request.getHttpInput()).thenReturn(httpInput);
+ when(request.getProtocol()).thenReturn("HTTP/1.1");
+ when(request.getScheme()).thenReturn(scheme);
+ when(request.getServerName()).thenReturn(serverName);
+ when(request.getRemoteAddr()).thenReturn(remoteAddress);
+ when(request.getRemotePort()).thenReturn(remotePort);
+ when(request.getRemoteHost()).thenReturn(remoteHost);
+ when(request.getLocalPort()).thenReturn(uriPort);
+ when(request.getMethod()).thenReturn("GET");
+ when(request.getQueryString()).thenReturn(query);
+ when(request.getRequestURI()).thenReturn(path);
+
+ mockCharacterEncodingHandling(request);
+ mockHeaderHandling(request);
+ mockParameterHandling(request);
+ mockAttributeHandling(request);
+
+ return request;
+ }
+
+ private void mockCharacterEncodingHandling(Request request) {
+ try {
+ AtomicReference<String> characterEncoding = new AtomicReference<>("");
+ when(request.getCharacterEncoding()).thenAnswer((Answer<String>) ignored -> characterEncoding.get());
+ doAnswer((Answer<Void>) invocation -> {
+ String value = invocation.getArgument(0);
+ characterEncoding.set(value);
+ return null;
+ }).when(request).setCharacterEncoding(anyString());
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void mockHeaderHandling(Request request) {
+ Map<String, List<String>> headers = new ConcurrentHashMap<>(this.headers);
+ when(request.getHeaderNames()).thenReturn(Collections.enumeration(headers.keySet()));
+ when(request.getHeaders(anyString())).thenAnswer((Answer<Enumeration<String>>) invocation -> {
+ String key = invocation.getArgument(0);
+ List<String> values = headers.get(key);
+ return values != null ? Collections.enumeration(values) : Collections.enumeration(List.of());
+ });
+ when(request.getHeader(anyString())).thenAnswer((Answer<String>) invocation -> {
+ String name = invocation.getArgument(0);
+ List<String> values = headers.get(name);
+ if (values == null || values.isEmpty()) return null;
+ return values.get(0);
+ });
+ }
+
+ private void mockParameterHandling(Request request) {
+ Map<String, String[]> parameters = new ConcurrentHashMap<>();
+ this.parameters.forEach((key, values) -> parameters.put(key, values.toArray(String[]::new)));
+ when(request.getParameterMap()).thenReturn(parameters);
+ }
+
+ private void mockAttributeHandling(Request request) {
+ Map<String, Object> attributes = new ConcurrentHashMap<>(this.attributes);
+
+ when(request.getAttribute(any())).thenAnswer(invocation -> {
+ String attributeName = invocation.getArgument(0);
+ return attributes.get(attributeName);
+ });
+ doAnswer((Answer<Void>) invocation -> {
+ String attributeName = invocation.getArgument(0);
+ Object attributeValue = invocation.getArgument(1);
+ attributes.put(attributeName, attributeValue);
+ return null;
+ }).when(request).setAttribute(anyString(), any());
+ doAnswer((Answer<Void>) invocation -> {
+ String attributeName = invocation.getArgument(0);
+ attributes.remove(attributeName);
+ return null;
+ }).when(request).removeAttribute(anyString());
+ }
+}
diff --git a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/JettyMockResponseBuilder.java b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/JettyMockResponseBuilder.java
new file mode 100644
index 00000000000..6addb966208
--- /dev/null
+++ b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/JettyMockResponseBuilder.java
@@ -0,0 +1,29 @@
+// 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 org.eclipse.jetty.http.MetaData;
+import org.eclipse.jetty.server.HttpChannel;
+import org.eclipse.jetty.server.Response;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * Builder for creating a mock instance of Jetty's {@link Response} type.
+ *
+ * @author bjorncs
+ */
+public class JettyMockResponseBuilder {
+
+ private JettyMockResponseBuilder() {}
+
+ public static JettyMockResponseBuilder newBuilder() { return new JettyMockResponseBuilder(); }
+
+ public Response build() {
+ 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/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/SimpleHttpClient.java b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/SimpleHttpClient.java
index eea8d7e3072..161f48d847d 100644
--- a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/SimpleHttpClient.java
+++ b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/SimpleHttpClient.java
@@ -1,33 +1,36 @@
// 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.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.apache.hc.client5.http.SystemDefaultDnsResolver;
+import org.apache.hc.client5.http.classic.methods.HttpGet;
+import org.apache.hc.client5.http.classic.methods.HttpPost;
+import org.apache.hc.client5.http.classic.methods.HttpUriRequest;
+import org.apache.hc.client5.http.entity.GzipCompressingEntity;
+import org.apache.hc.client5.http.entity.mime.FormBodyPart;
+import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
+import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
+import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
+import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
+import org.apache.hc.client5.http.ssl.DefaultHostnameVerifier;
+import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.HttpResponse;
+import org.apache.hc.core5.http.ParseException;
+import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.apache.hc.core5.http.io.entity.StringEntity;
import org.hamcrest.Matcher;
import org.hamcrest.MatcherAssert;
import javax.net.ssl.SSLContext;
import java.io.IOException;
+import java.net.InetAddress;
import java.net.URI;
+import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
@@ -55,8 +58,9 @@ public class SimpleHttpClient implements AutoCloseable {
public SimpleHttpClient(SSLContext sslContext, List<String> enabledProtocols, List<String> enabledCiphers,
int listenPort, boolean useCompression) {
- HttpClientBuilder builder = HttpClientBuilder.create();
- builder.disableConnectionState(); // Reuse SSL connection when client authentication is enabled
+ HttpClientBuilder builder = HttpClientBuilder.create()
+ .disableAutomaticRetries()
+ .disableConnectionState(); // Reuse SSL connection when client authentication is enabled
if (!useCompression) {
builder.disableContentCompression();
}
@@ -66,12 +70,17 @@ public class SimpleHttpClient implements AutoCloseable {
toArray(enabledProtocols),
toArray(enabledCiphers),
new DefaultHostnameVerifier());
- builder.setSSLSocketFactory(sslConnectionFactory);
-
- Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
- .register("https", sslConnectionFactory)
+ PoolingHttpClientConnectionManager connManager = PoolingHttpClientConnectionManagerBuilder.create()
+ .setSSLSocketFactory(sslConnectionFactory)
+ .setDnsResolver(new SystemDefaultDnsResolver() {
+ @Override
+ public InetAddress[] resolve(String host) throws UnknownHostException {
+ // Returns single address instead of multiple (to avoid multiple connection attempts)
+ return new InetAddress[] { InetAddress.getByName(host) };
+ }
+ })
.build();
- builder.setConnectionManager(new BasicHttpClientConnectionManager(registry));
+ builder.setConnectionManager(connManager);
scheme = "https";
} else {
scheme = "http";
@@ -139,7 +148,7 @@ public class SimpleHttpClient implements AutoCloseable {
}
public RequestExecutor setBinaryContent(final byte[] content) {
- this.entity = new ByteArrayEntity(content);
+ this.entity = new ByteArrayEntity(content, ContentType.DEFAULT_BINARY);
return this;
}
@@ -152,7 +161,7 @@ public class SimpleHttpClient implements AutoCloseable {
public ResponseValidator execute() throws IOException {
if (entity != null) {
- ((HttpPost)request).setEntity(entity);
+ request.setEntity(entity);
}
try (CloseableHttpResponse response = delegate.execute(request)){
return new ResponseValidator(response);
@@ -165,15 +174,19 @@ public class SimpleHttpClient implements AutoCloseable {
private final HttpResponse response;
private final String content;
- public ResponseValidator(HttpResponse response) throws IOException {
- this.response = response;
+ public ResponseValidator(CloseableHttpResponse response) throws IOException {
+ try {
+ this.response = response;
- HttpEntity entity = response.getEntity();
- this.content = entity == null ? null : EntityUtils.toString(entity, StandardCharsets.UTF_8);
+ HttpEntity entity = response.getEntity();
+ this.content = entity == null ? null : EntityUtils.toString(entity, StandardCharsets.UTF_8);
+ } catch (ParseException e) {
+ throw new IOException(e);
+ }
}
public ResponseValidator expectStatusCode(Matcher<Integer> matcher) {
- MatcherAssert.assertThat(response.getStatusLine().getStatusCode(), matcher);
+ MatcherAssert.assertThat(response.getCode(), matcher);
return this;
}
diff --git a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/TestDrivers.java b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/TestDrivers.java
index 7d7530c32e0..75fc0948da9 100644
--- a/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/TestDrivers.java
+++ b/container-core/src/test/java/com/yahoo/jdisc/http/server/jetty/TestDrivers.java
@@ -55,10 +55,7 @@ public class TestDrivers {
newConfigModule(
new ServerConfig.Builder().connectionLog(new ServerConfig.ConnectionLog.Builder().enabled(true)),
new ConnectorConfig.Builder()
- .tlsClientAuthEnforcer(
- new ConnectorConfig.TlsClientAuthEnforcer.Builder()
- .enable(true)
- .pathWhitelist("/status.html"))
+ .http2Enabled(true)
.ssl(new ConnectorConfig.Ssl.Builder()
.enabled(true)
.clientAuth(tlsClientAuth == TlsClientAuth.NEED