summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--container/pom.xml4
-rw-r--r--http-utils/pom.xml5
-rw-r--r--http-utils/src/main/java/ai/vespa/util/http/VespaAsyncHttpClientBuilder.java93
-rw-r--r--parent/pom.xml6
4 files changed, 108 insertions, 0 deletions
diff --git a/container/pom.xml b/container/pom.xml
index cf3aa21513b..4f045aac90e 100644
--- a/container/pom.xml
+++ b/container/pom.xml
@@ -45,6 +45,10 @@
<groupId>io.airlift</groupId>
<artifactId>airline</artifactId>
</exclusion>
+ <exclusion>
+ <groupId>org.apache.httpcomponents.client5</groupId>
+ <artifactId>httpclient5</artifactId>
+ </exclusion>
</exclusions>
</dependency>
</dependencies>
diff --git a/http-utils/pom.xml b/http-utils/pom.xml
index 6d2e009cf8c..aa261574285 100644
--- a/http-utils/pom.xml
+++ b/http-utils/pom.xml
@@ -38,6 +38,11 @@
<artifactId>httpcore</artifactId>
<scope>compile</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents.client5</groupId>
+ <artifactId>httpclient5</artifactId>
+ <scope>compile</scope>
+ </dependency>
<!-- test scope -->
<dependency>
diff --git a/http-utils/src/main/java/ai/vespa/util/http/VespaAsyncHttpClientBuilder.java b/http-utils/src/main/java/ai/vespa/util/http/VespaAsyncHttpClientBuilder.java
new file mode 100644
index 00000000000..00f16e5d7c7
--- /dev/null
+++ b/http-utils/src/main/java/ai/vespa/util/http/VespaAsyncHttpClientBuilder.java
@@ -0,0 +1,93 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package ai.vespa.util.http;
+
+import com.yahoo.security.tls.MixedMode;
+import com.yahoo.security.tls.TlsContext;
+import com.yahoo.security.tls.TransportSecurityUtils;
+import org.apache.hc.client5.http.HttpRoute;
+import org.apache.hc.client5.http.impl.DefaultSchemePortResolver;
+import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
+import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
+import org.apache.hc.client5.http.impl.routing.DefaultRoutePlanner;
+import org.apache.hc.client5.http.nio.AsyncClientConnectionManager;
+import org.apache.hc.client5.http.routing.HttpRoutePlanner;
+import org.apache.hc.client5.http.ssl.ClientTlsStrategyBuilder;
+import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
+import org.apache.hc.core5.http.protocol.HttpContext;
+
+import javax.net.ssl.SSLParameters;
+
+/**
+ * Async http client builder for internal Vespa communications over http/https.
+ * Configures Vespa mTLS and handles TLS mixed mode automatically.
+ * Client should only be used for requests to Vespa services.
+ *
+ * Caveats:
+ * - custom connection manager must be configured through {@link #create(AsyncConnectionManagerFactory)}.
+ *
+ * @author bjorncs
+ */
+public class VespaAsyncHttpClientBuilder {
+
+ public interface AsyncConnectionManagerFactory {
+ AsyncClientConnectionManager create(TlsStrategy tlsStrategy);
+ }
+
+ public static HttpAsyncClientBuilder create() {
+ return create(
+ tlsStrategy -> PoolingAsyncClientConnectionManagerBuilder.create()
+ .setTlsStrategy(tlsStrategy)
+ .build());
+ }
+
+ public static HttpAsyncClientBuilder create(AsyncConnectionManagerFactory factory) {
+ HttpAsyncClientBuilder clientBuilder = HttpAsyncClientBuilder.create();
+ TlsContext vespaTlsContext = TransportSecurityUtils.createTlsContext().orElse(null);
+ TlsStrategy tlsStrategy;
+ if (vespaTlsContext != null) {
+ SSLParameters vespaTlsParameters = vespaTlsContext.parameters();
+ tlsStrategy = ClientTlsStrategyBuilder.create()
+ .setHostnameVerifier(new NoopHostnameVerifier())
+ .setSslContext(vespaTlsContext.context())
+ .setTlsVersions(vespaTlsParameters.getProtocols())
+ .setCiphers(vespaTlsParameters.getCipherSuites())
+ .build();
+ if (TransportSecurityUtils.getInsecureMixedMode() != MixedMode.PLAINTEXT_CLIENT_MIXED_SERVER) {
+ clientBuilder.setRoutePlanner(new HttpToHttpsRoutePlanner());
+ }
+ } else {
+ tlsStrategy = ClientTlsStrategyBuilder.create().build();
+ }
+ clientBuilder.disableConnectionState(); // Share connections between subsequent requests
+ clientBuilder.disableCookieManagement();
+ clientBuilder.setConnectionManager(factory.create(tlsStrategy));
+ return clientBuilder;
+ }
+
+ private static class HttpToHttpsRoutePlanner implements HttpRoutePlanner {
+
+ private final DefaultRoutePlanner defaultPlanner = new DefaultRoutePlanner(new DefaultSchemePortResolver());
+
+ @Override
+ public HttpRoute determineRoute(HttpHost target, HttpContext context) throws HttpException {
+ HttpRoute originalRoute = defaultPlanner.determineRoute(target, context);
+ HttpHost originalHost = originalRoute.getTargetHost();
+ String originalScheme = originalHost.getSchemeName();
+ String rewrittenScheme = originalScheme.equalsIgnoreCase("http") ? "https" : originalScheme;
+ boolean rewrittenSecure = target.getSchemeName().equalsIgnoreCase("https");
+ HttpHost rewrittenHost = new HttpHost(
+ rewrittenScheme, originalHost.getAddress(), originalHost.getHostName(), originalHost.getPort());
+ return new HttpRoute(
+ rewrittenHost,
+ originalRoute.getLocalAddress(),
+ originalRoute.getProxyHost(),
+ rewrittenSecure,
+ originalRoute.getTunnelType(),
+ originalRoute.getLayerType());
+ }
+ }
+
+}
diff --git a/parent/pom.xml b/parent/pom.xml
index cac59966332..72aa330fe13 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -575,6 +575,11 @@
<version>${apache.httpcore.version}</version>
</dependency>
<dependency>
+ <groupId>org.apache.httpcomponents.client5</groupId>
+ <artifactId>httpclient5</artifactId>
+ <version>${apache.httpclient5.version}</version>
+ </dependency>
+ <dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.3.6</version>
@@ -749,6 +754,7 @@
<antlr4.version>4.5</antlr4.version>
<apache.httpclient.version>4.5.12</apache.httpclient.version>
<apache.httpcore.version>4.4.13</apache.httpcore.version>
+ <apache.httpclient5.version>5.0.3</apache.httpclient5.version>
<asm.version>7.0</asm.version>
<!-- Athenz dependencies. Make sure these dependencies match those in Vespa's internal repositories -->
<athenz.version>1.8.49</athenz.version>