aboutsummaryrefslogtreecommitdiffstats
path: root/configserver-client
diff options
context:
space:
mode:
authorjonmv <venstad@gmail.com>2022-04-28 10:12:06 +0200
committerjonmv <venstad@gmail.com>2022-04-28 10:12:06 +0200
commit1a4c0d52ab509782dacf128624665789c9b0f59b (patch)
tree6de8bb1c6bfa1e4f8f8792146a03d93881933b37 /configserver-client
parentcd04e0c3a06499971c82678e88510b257d4d6faa (diff)
Split out the general part of the configserver-client
Diffstat (limited to 'configserver-client')
-rw-r--r--configserver-client/OWNERS1
-rw-r--r--configserver-client/README.md2
-rw-r--r--configserver-client/pom.xml75
-rw-r--r--configserver-client/src/main/java/ai/vespa/hosted/client/AbstractConfigServerClient.java259
-rw-r--r--configserver-client/src/main/java/ai/vespa/hosted/client/ConfigServerClient.java278
-rw-r--r--configserver-client/src/main/java/ai/vespa/hosted/client/ForwardingInputStream.java55
-rw-r--r--configserver-client/src/main/java/ai/vespa/hosted/client/HttpConfigServerClient.java66
-rw-r--r--configserver-client/src/main/java/ai/vespa/hosted/client/MockConfigServerClient.java57
-rw-r--r--configserver-client/src/test/java/ai/vespa/hosted/client/HttpConfigServerClientTest.java98
-rw-r--r--configserver-client/src/test/java/ai/vespa/hosted/client/WireMockExtension.java42
10 files changed, 0 insertions, 933 deletions
diff --git a/configserver-client/OWNERS b/configserver-client/OWNERS
deleted file mode 100644
index d0a102ecbf4..00000000000
--- a/configserver-client/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-jonmv
diff --git a/configserver-client/README.md b/configserver-client/README.md
deleted file mode 100644
index 7f124bbb3ec..00000000000
--- a/configserver-client/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-<!-- Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
-# HTTP client for configserver APIs, built on Apache http client 5
diff --git a/configserver-client/pom.xml b/configserver-client/pom.xml
deleted file mode 100644
index 91ac43c72f6..00000000000
--- a/configserver-client/pom.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -->
-<project xmlns="http://maven.apache.org/POM/4.0.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <artifactId>parent</artifactId>
- <groupId>com.yahoo.vespa</groupId>
- <version>7-SNAPSHOT</version>
- <relativePath>../parent/pom.xml</relativePath>
- </parent>
- <artifactId>configserver-client</artifactId>
- <description>HTTP client for configserver APIs, built on Apache http client 5</description>
-
- <dependencies>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>http-utils</artifactId>
- <version>${project.version}</version>
- <scope>compile</scope>
- </dependency>
-
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>vespa-athenz</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>container-apache-http-client-bundle</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>security-utils</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>config-provisioning</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>vespajlib</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>com.yahoo.vespa</groupId>
- <artifactId>yolean</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
-
- <dependency>
- <groupId>org.junit.jupiter</groupId>
- <artifactId>junit-jupiter</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>com.github.tomakehurst</groupId>
- <artifactId>wiremock-standalone</artifactId>
- <version>2.27.2</version>
- <scope>test</scope>
- </dependency>
- </dependencies>
-</project>
-
diff --git a/configserver-client/src/main/java/ai/vespa/hosted/client/AbstractConfigServerClient.java b/configserver-client/src/main/java/ai/vespa/hosted/client/AbstractConfigServerClient.java
deleted file mode 100644
index 3422d96e1d3..00000000000
--- a/configserver-client/src/main/java/ai/vespa/hosted/client/AbstractConfigServerClient.java
+++ /dev/null
@@ -1,259 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package ai.vespa.hosted.client;
-
-import ai.vespa.hosted.client.ConfigServerClient.RequestBuilder;
-import ai.vespa.http.HttpURL;
-import ai.vespa.http.HttpURL.Path;
-import ai.vespa.http.HttpURL.Query;
-import org.apache.hc.client5.http.config.RequestConfig;
-import org.apache.hc.client5.http.protocol.HttpClientContext;
-import org.apache.hc.core5.http.ClassicHttpRequest;
-import org.apache.hc.core5.http.ClassicHttpResponse;
-import org.apache.hc.core5.http.ContentType;
-import org.apache.hc.core5.http.HttpEntity;
-import org.apache.hc.core5.http.Method;
-import org.apache.hc.core5.http.ParseException;
-import org.apache.hc.core5.http.io.entity.EntityUtils;
-import org.apache.hc.core5.http.io.entity.HttpEntities;
-import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
-
-import java.io.IOException;
-import java.io.UncheckedIOException;
-import java.net.URI;
-import java.time.Duration;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-import java.util.function.BiFunction;
-import java.util.function.Function;
-import java.util.logging.Logger;
-
-import static java.util.Objects.requireNonNull;
-import static java.util.logging.Level.FINE;
-import static java.util.logging.Level.WARNING;
-
-/**
- * @author jonmv
- */
-public abstract class AbstractConfigServerClient implements ConfigServerClient {
-
- private static final Logger log = Logger.getLogger(AbstractConfigServerClient.class.getName());
-
- /** Executes the request with the given context. The caller must close the response. */
- abstract ClassicHttpResponse execute(ClassicHttpRequest request, HttpClientContext context) throws IOException;
-
- /** Executes the given request with response/error handling and retries. */
- private <T> T execute(RequestBuilder builder,
- BiFunction<ClassicHttpResponse, ClassicHttpRequest, T> handler,
- ExceptionHandler catcher) {
- HttpClientContext context = HttpClientContext.create();
- context.setRequestConfig(builder.config);
-
- Throwable thrown = null;
- for (URI host : builder.hosts) {
- ClassicHttpRequest request = ClassicRequestBuilder.create(builder.method.name())
- .setUri(HttpURL.from(host)
- .appendPath(builder.path)
- .appendQuery(builder.query)
- .asURI())
- .build();
- request.setEntity(builder.entity);
- try {
- try {
- return handler.apply(execute(request, context), request);
- }
- catch (IOException e) {
- catcher.handle(e, request);
- throw RetryException.wrap(e, request);
- }
- }
- catch (RetryException e) {
- if (thrown == null)
- thrown = e.getCause();
- else
- thrown.addSuppressed(e.getCause());
-
- if (builder.entity != null && ! builder.entity.isRepeatable()) {
- log.log(WARNING, "Cannot retry " + request + " as entity is not repeatable");
- break;
- }
- log.log(FINE, e.getCause(), () -> request + " failed; will retry");
- }
- }
- if (thrown != null) {
- if (thrown instanceof IOException)
- throw new UncheckedIOException((IOException) thrown);
- else if (thrown instanceof RuntimeException)
- throw (RuntimeException) thrown;
- else
- throw new IllegalStateException("Illegal retry cause: " + thrown.getClass(), thrown);
- }
-
- throw new IllegalStateException("No hosts to perform the request against");
- }
-
- @Override
- public ConfigServerClient.RequestBuilder send(HostStrategy hosts, Method method) {
- return new RequestBuilder(hosts, method);
- }
-
- /** Builder for a request against a given set of hosts. */
- class RequestBuilder implements ConfigServerClient.RequestBuilder {
-
- private final Method method;
- private final HostStrategy hosts;
- private HttpURL.Path path = Path.empty();
- private HttpURL.Query query = Query.empty();
- private HttpEntity entity;
- private RequestConfig config = ConfigServerClient.defaultRequestConfig;
- private ResponseVerifier verifier = ConfigServerClient.throwOnError;
- private ExceptionHandler catcher = ConfigServerClient.retryAll;
-
- private RequestBuilder(HostStrategy hosts, Method method) {
- if ( ! hosts.iterator().hasNext())
- throw new IllegalArgumentException("Host strategy cannot be empty");
-
- this.hosts = hosts;
- this.method = requireNonNull(method);
- }
-
- @Override
- public RequestBuilder at(Path subPath) {
- path = path.append(subPath);
- return this;
- }
-
- @Override
- public ConfigServerClient.RequestBuilder body(byte[] json) {
- return body(HttpEntities.create(json, ContentType.APPLICATION_JSON));
- }
-
- @Override
- public RequestBuilder body(HttpEntity entity) {
- this.entity = requireNonNull(entity);
- return this;
- }
-
- @Override
- public ConfigServerClient.RequestBuilder emptyParameters(List<String> keys) {
- for (String key : keys)
- query = query.add(key);
-
- return this;
- }
-
- @Override
- public RequestBuilder parameters(List<String> pairs) {
- if (pairs.size() % 2 != 0)
- throw new IllegalArgumentException("Must supply parameter key/values in pairs");
-
- for (int i = 0; i < pairs.size(); ) {
- String key = pairs.get(i++), value = pairs.get(i++);
- if (value != null)
- query = query.add(key, value);
- }
-
- return this;
- }
-
- @Override
- public ConfigServerClient.RequestBuilder parameters(Query query) {
- this.query = this.query.add(query.entries());
- return this;
- }
-
- @Override
- public RequestBuilder timeout(Duration timeout) {
- return config(RequestConfig.copy(config)
- .setResponseTimeout(timeout.toMillis(), TimeUnit.MILLISECONDS)
- .build());
- }
-
- @Override
- public RequestBuilder config(RequestConfig config) {
- this.config = requireNonNull(config);
- return this;
- }
-
- @Override
- public RequestBuilder catching(ExceptionHandler catcher) {
- this.catcher = requireNonNull(catcher);
- return this;
- }
-
- @Override
- public RequestBuilder throwing(ResponseVerifier verifier) {
- this.verifier = requireNonNull(verifier);
- return this;
- }
-
- @Override
- public String read() {
- return handle((response, __) -> {
- try (response) {
- return response.getEntity() == null ? "" : EntityUtils.toString(response.getEntity());
- }
- catch (ParseException e) {
- throw new IllegalStateException(e); // This isn't actually thrown by apache >_<
- }
- });
- }
-
- @Override
- public <T> T read(Function<byte[], T> mapper) {
- return handle((response, __) -> {
- try (response) {
- return mapper.apply(response.getEntity() == null ? new byte[0] : EntityUtils.toByteArray(response.getEntity()));
- }
- });
- }
-
- @Override
- public void discard() throws UncheckedIOException, ResponseException {
- handle((response, __) -> {
- try (response) {
- return null;
- }
- });
- }
-
- @Override
- public HttpInputStream stream() throws UncheckedIOException, ResponseException {
- return handle((response, __) -> new HttpInputStream(response));
- }
-
- @Override
- public <T> T handle(ResponseHandler<T> handler) {
- return execute(this,
- (response, request) -> {
- try {
- verifier.verify(response, request); // This throws on unacceptable responses.
- return handler.handle(response, request);
- }
- catch (IOException | RuntimeException | Error e) {
- try {
- response.close();
- }
- catch (IOException f) {
- e.addSuppressed(f);
- }
- if (e instanceof IOException) {
- catcher.handle((IOException) e, request);
- throw RetryException.wrap((IOException) e, request);
- }
- else
- sneakyThrow(e); // e is a runtime exception or an error, so this is fine.
- throw new AssertionError("Should not happen");
- }
- },
- catcher);
- }
-
- }
-
-
- @SuppressWarnings("unchecked")
- private static <T extends Throwable> void sneakyThrow(Throwable t) throws T {
- throw (T) t;
- }
-
-} \ No newline at end of file
diff --git a/configserver-client/src/main/java/ai/vespa/hosted/client/ConfigServerClient.java b/configserver-client/src/main/java/ai/vespa/hosted/client/ConfigServerClient.java
deleted file mode 100644
index 47e062766c6..00000000000
--- a/configserver-client/src/main/java/ai/vespa/hosted/client/ConfigServerClient.java
+++ /dev/null
@@ -1,278 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package ai.vespa.hosted.client;
-
-import ai.vespa.http.HttpURL;
-import ai.vespa.http.HttpURL.Path;
-import ai.vespa.http.HttpURL.Query;
-import org.apache.hc.client5.http.config.RequestConfig;
-import org.apache.hc.core5.http.ClassicHttpRequest;
-import org.apache.hc.core5.http.ClassicHttpResponse;
-import org.apache.hc.core5.http.HttpEntity;
-import org.apache.hc.core5.http.HttpStatus;
-import org.apache.hc.core5.http.Method;
-import org.apache.hc.core5.http.io.entity.EntityUtils;
-import org.apache.hc.core5.util.Timeout;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.UncheckedIOException;
-import java.net.URI;
-import java.time.Duration;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.function.Function;
-import java.util.stream.IntStream;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-import static java.util.Objects.requireNonNull;
-import static java.util.stream.Collectors.toUnmodifiableList;
-
-/**
- * @author jonmv
- */
-public interface ConfigServerClient extends Closeable {
-
- RequestConfig defaultRequestConfig = RequestConfig.custom()
- .setConnectionRequestTimeout(Timeout.ofSeconds(5))
- .setConnectTimeout(Timeout.ofSeconds(5))
- .setRedirectsEnabled(false)
- .build();
-
- /** Does nothing, letting the client wrap with a {@link RetryException} and re-throw. */
- ExceptionHandler retryAll = (exception, request) -> { };
-
- /** Throws a a {@link RetryException} if {@code statusCode == 503}, or a {@link ResponseException} unless {@code 200 <= statusCode < 300}. */
- ResponseVerifier throwOnError = new DefaultResponseVerifier() { };
-
- /** Reads the response body, throwing an {@link UncheckedIOException} if this fails, or {@code null} if there is none. */
- static byte[] getBytes(ClassicHttpResponse response) {
- try {
- return response.getEntity() == null ? null : EntityUtils.toByteArray(response.getEntity());
- }
- catch (IOException e) {
- throw new UncheckedIOException(e);
- }
- }
-
- /** Creates a builder for sending the given method, using the specified host strategy. */
- RequestBuilder send(HostStrategy hosts, Method method);
-
- /** Builder for a request against a given set of hosts, using this config server client. */
- interface RequestBuilder {
-
- /** Appends to the request path, with no trailing slash. */
- default RequestBuilder at(String... pathSegments) { return at(List.of(pathSegments)); }
-
- /** Appends to the request path, with no trailing slash. */
- default RequestBuilder at(List<String> pathSegments) { return at(Path.empty().append(pathSegments).withoutTrailingSlash()); }
-
- /** Appends to the request path. */
- RequestBuilder at(HttpURL.Path path);
-
- /** Sets the request body as UTF-8 application/json. */
- RequestBuilder body(byte[] json);
-
- /** Sets the request body. */
- RequestBuilder body(HttpEntity entity);
-
- /** Sets query parameters without a value, like {@code ?debug&recursive}. */
- default RequestBuilder emptyParameters(String... keys) {
- return emptyParameters(Arrays.asList(keys));
- }
-
- /** Sets query parameters without a value, like {@code ?debug&recursive}. */
- RequestBuilder emptyParameters(List<String> keys);
-
- /** Sets the parameter key/values for the request. Number of arguments must be even. Null values are omitted. */
- default RequestBuilder parameters(String... pairs) {
- return parameters(Arrays.asList(pairs));
- }
-
- /** Sets the parameter key/values for the request. Number of arguments must be even. Pairs with {@code null} values are omitted. */
- RequestBuilder parameters(List<String> pairs);
-
- /** Appends all parameters from the given query. */
- RequestBuilder parameters(Query query);
-
- /** Overrides the default socket read timeout of the request. {@code Duration.ZERO} gives infinite timeout. */
- RequestBuilder timeout(Duration timeout);
-
- /** Overrides the default request config of the request. */
- RequestBuilder config(RequestConfig config);
-
- /**
- * Sets the catch clause for {@link IOException}s during execution of this.
- * The default is to wrap the IOException in a {@link RetryException} and rethrow this;
- * this makes the client retry the request, as long as there are remaining entries in the {@link HostStrategy}.
- * If the catcher returns normally, the exception is wrapped and retried, as per the default.
- */
- RequestBuilder catching(ExceptionHandler catcher);
-
- /**
- * Sets the (error) response handler for this request. The default is {@link #throwOnError}.
- * When the handler returns normally, the response is treated as a success, and passed on to a response mapper.
- */
- RequestBuilder throwing(ResponseVerifier handler);
-
- /** Reads the response as a {@link String}, or throws if unsuccessful. */
- String read();
-
- /** Reads and maps the response, or throws if unsuccessful. */
- <T> T read(Function<byte[], T> mapper);
-
- /** Discards the response, but throws if unsuccessful. */
- void discard();
-
- /** Returns the raw response input stream, or throws if unsuccessful. The caller must close the returned stream. */
- HttpInputStream stream();
-
- /** Uses the response and request, if successful, to generate a mapped response. */
- <T> T handle(ResponseHandler<T> handler);
-
- }
-
-
- class HttpInputStream extends ForwardingInputStream {
-
- private final ClassicHttpResponse response;
-
- protected HttpInputStream(ClassicHttpResponse response) throws IOException {
- super(response.getEntity() != null ? response.getEntity().getContent()
- : InputStream.nullInputStream());
- this.response = response;
- }
-
- public int statusCode() { return response.getCode(); }
-
- public String contentType() { return response.getEntity().getContentType(); }
-
- @Override
- public void close() throws IOException {
- super.close();
- response.close();
- }
-
- }
-
-
- /** Reads a successful response and request to compute a result. */
- @FunctionalInterface
- interface ResponseHandler<T> {
-
- /** Called with successful responses, as per {@link ResponseVerifier}. The caller must close the response. */
- T handle(ClassicHttpResponse response, ClassicHttpRequest request) throws IOException;
-
- }
-
-
- /** Verifies a response, throwing on error responses, possibly indicating retries. */
- @FunctionalInterface
- interface ResponseVerifier {
-
- /** Whether this status code means the response is an error response. */
- default boolean isError(int statusCode) {
- return statusCode < HttpStatus.SC_OK || HttpStatus.SC_REDIRECTION <= statusCode;
- }
-
- /** Whether this status code means we should retry. Has no effect if this is not also an error. */
- default boolean shouldRetry(int statusCode) {
- return statusCode == HttpStatus.SC_SERVICE_UNAVAILABLE;
- }
-
- /** Verifies the given response, consuming it and throwing if it is an error; or leaving it otherwise. */
- default void verify(ClassicHttpResponse response, ClassicHttpRequest request) throws IOException {
- if (isError(response.getCode())) {
- try (response) {
- byte[] body = response.getEntity() == null ? new byte[0] : EntityUtils.toByteArray(response.getEntity());
- RuntimeException exception = toException(response.getCode(), body, request);
- throw shouldRetry(response.getCode()) ? new RetryException(exception) : exception;
- }
- }
- }
-
- /** Throws the appropriate exception, for the given status code and body. */
- RuntimeException toException(int statusCode, byte[] body, ClassicHttpRequest request);
-
- }
-
-
- interface DefaultResponseVerifier extends ResponseVerifier {
-
- @Override
- default RuntimeException toException(int statusCode, byte[] body, ClassicHttpRequest request) {
- return new ResponseException(request + " failed with status " + statusCode + " and body '" + new String(body, UTF_8) + "'");
- }
-
- }
-
-
- @FunctionalInterface
- interface ExceptionHandler {
-
- /**
- * Called with any IO exception that might occur when attempting to send the request.
- * To retry, wrap the exception with a {@link RetryException} and re-throw, or exit normally.
- * Any other thrown exception will propagate out to the caller.
- */
- void handle(IOException exception, ClassicHttpRequest request);
-
- }
-
-
- /** What host(s) to try for a request, in what order. A host may be specified multiple times, for retries. */
- @FunctionalInterface
- interface HostStrategy extends Iterable<URI> {
-
- /** Attempts each request once against each listed host. */
- static HostStrategy ordered(List<URI> hosts) {
- return List.copyOf(hosts)::iterator;
- }
-
- /** Attempts each request once against each listed host, in random order. */
- static HostStrategy shuffling(List<URI> hosts) {
- return () -> {
- List<URI> copy = new ArrayList<>(hosts);
- Collections.shuffle(copy);
- return copy.iterator();
- };
- }
-
- /** Attempts each request against the host the specified number of times. */
- static HostStrategy repeating(URI host, int count) {
- return ordered(IntStream.range(0, count).mapToObj(__ -> host).collect(toUnmodifiableList()));
- }
-
- }
-
-
- /** Exception wrapper that signals retries should be attempted. */
- final class RetryException extends RuntimeException {
-
- public RetryException(IOException cause) {
- super(requireNonNull(cause));
- }
-
- public RetryException(RuntimeException cause) {
- super(requireNonNull(cause));
- }
-
- static RetryException wrap(IOException exception, ClassicHttpRequest request) {
- return new RetryException(new UncheckedIOException(request + " failed (" + exception.getClass().getSimpleName() + ")", exception));
- }
-
- }
-
-
- /** An exception due to server error, a bad request, or similar, which resulted in a non-OK HTTP response. */
- class ResponseException extends RuntimeException {
-
- public ResponseException(String message) {
- super(message);
- }
-
- }
-
-} \ No newline at end of file
diff --git a/configserver-client/src/main/java/ai/vespa/hosted/client/ForwardingInputStream.java b/configserver-client/src/main/java/ai/vespa/hosted/client/ForwardingInputStream.java
deleted file mode 100644
index 402c2689ca7..00000000000
--- a/configserver-client/src/main/java/ai/vespa/hosted/client/ForwardingInputStream.java
+++ /dev/null
@@ -1,55 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package ai.vespa.hosted.client;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-import static java.util.Objects.requireNonNull;
-
-/**
- * @author jonmv
- */
-public class ForwardingInputStream extends InputStream {
-
- private final InputStream delegate;
-
- public ForwardingInputStream(InputStream delegate) {
- this.delegate = requireNonNull(delegate);
- }
-
- @Override
- public int read() throws IOException {
- return delegate.read();
- }
-
- @Override
- public int read(byte[] b, int off, int len) throws IOException {
- return delegate.read(b, off, len);
- }
-
- @Override
- public int available() throws IOException {
- return delegate.available();
- }
-
- @Override
- public void close() throws IOException {
- delegate.close();
- }
-
- @Override
- public synchronized void mark(int readlimit) {
- delegate.mark(readlimit);
- }
-
- @Override
- public synchronized void reset() throws IOException {
- delegate.reset();
- }
-
- @Override
- public boolean markSupported() {
- return delegate.markSupported();
- }
-
-}
diff --git a/configserver-client/src/main/java/ai/vespa/hosted/client/HttpConfigServerClient.java b/configserver-client/src/main/java/ai/vespa/hosted/client/HttpConfigServerClient.java
deleted file mode 100644
index 6365e2b6b7d..00000000000
--- a/configserver-client/src/main/java/ai/vespa/hosted/client/HttpConfigServerClient.java
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package ai.vespa.hosted.client;
-
-import ai.vespa.util.http.hc5.VespaHttpClientBuilder;
-import com.yahoo.vespa.athenz.api.AthenzIdentity;
-import com.yahoo.vespa.athenz.tls.AthenzIdentityVerifier;
-import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
-import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
-import org.apache.hc.client5.http.protocol.HttpClientContext;
-import org.apache.hc.core5.http.ClassicHttpRequest;
-import org.apache.hc.core5.http.ClassicHttpResponse;
-import org.apache.hc.core5.http.io.SocketConfig;
-import org.apache.hc.core5.util.TimeValue;
-import org.apache.hc.core5.util.Timeout;
-
-import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.SSLSession;
-import java.io.IOException;
-import java.util.Collection;
-import java.util.Set;
-
-/**
- * @author jonmv
- */
-public class HttpConfigServerClient extends AbstractConfigServerClient {
-
- private final CloseableHttpClient client;
-
- public HttpConfigServerClient(Collection<AthenzIdentity> serverIdentities, String userAgent) {
- if (serverIdentities.isEmpty())
- throw new IllegalArgumentException("At least one trusted server identity must be provided");
-
- this.client = createClient(serverIdentities, userAgent);
- }
-
- @Override
- public void close() throws IOException {
- client.close();
- }
-
- @Override
- protected ClassicHttpResponse execute(ClassicHttpRequest request, HttpClientContext context) throws IOException {
- return client.execute(request, context);
- }
-
- private static CloseableHttpClient createClient(Collection<AthenzIdentity> serverIdentities, String userAgent) {
- return VespaHttpClientBuilder.create(socketFactories -> {
- var manager = new PoolingHttpClientConnectionManager(socketFactories);
- manager.setMaxTotal(1024);
- manager.setDefaultMaxPerRoute(128);
- manager.setDefaultSocketConfig(SocketConfig.custom().setSoTimeout(Timeout.ofSeconds(5)).build());
- manager.setValidateAfterInactivity(TimeValue.ofSeconds(10));
- return manager;
- },
- new AthenzIdentityVerifier(Set.copyOf(serverIdentities)) {
- @Override public boolean verify(String hostname, SSLSession session) {
- return super.verify(hostname, session) || "localhost".equals(hostname);
- }
- },
- false)
- .disableAutomaticRetries()
- .setUserAgent(userAgent)
- .build();
- }
-
-}
diff --git a/configserver-client/src/main/java/ai/vespa/hosted/client/MockConfigServerClient.java b/configserver-client/src/main/java/ai/vespa/hosted/client/MockConfigServerClient.java
deleted file mode 100644
index 16aad80670b..00000000000
--- a/configserver-client/src/main/java/ai/vespa/hosted/client/MockConfigServerClient.java
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package ai.vespa.hosted.client;
-
-import org.apache.hc.client5.http.protocol.HttpClientContext;
-import org.apache.hc.core5.http.ClassicHttpRequest;
-import org.apache.hc.core5.http.ClassicHttpResponse;
-import org.apache.hc.core5.http.ContentType;
-import org.apache.hc.core5.http.io.entity.HttpEntities;
-import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
-
-import java.io.IOException;
-import java.util.ArrayDeque;
-import java.util.Deque;
-import java.util.function.Function;
-
-/**
- * @author jonmv
- */
-public class MockConfigServerClient extends AbstractConfigServerClient {
-
- private final Deque<Expectation> expectations = new ArrayDeque<>();
-
- @Override
- protected ClassicHttpResponse execute(ClassicHttpRequest request, HttpClientContext context) throws IOException {
- Expectation expectation = expectations.poll();
- if (expectation == null)
- throw new AssertionError("No further requests expected, but got " + request);
-
- return expectation.handle(request);
- }
-
- @Override
- public void close() {
- if ( ! expectations.isEmpty())
- throw new AssertionError(expectations.size() + " more requests were expected");
- }
-
- public void expect(Expectation expectation) {
- expectations.add(expectation);
- }
-
- public void expect(int status, Function<ClassicHttpRequest, String> mapper) {
- expect(request -> {
- BasicClassicHttpResponse response = new BasicClassicHttpResponse(status);
- response.setEntity(HttpEntities.create(mapper.apply(request), ContentType.APPLICATION_JSON));
- return response;
- });
- }
-
- @FunctionalInterface
- public interface Expectation {
-
- ClassicHttpResponse handle(ClassicHttpRequest request) throws IOException;
-
- }
-
-}
diff --git a/configserver-client/src/test/java/ai/vespa/hosted/client/HttpConfigServerClientTest.java b/configserver-client/src/test/java/ai/vespa/hosted/client/HttpConfigServerClientTest.java
deleted file mode 100644
index e969201605e..00000000000
--- a/configserver-client/src/test/java/ai/vespa/hosted/client/HttpConfigServerClientTest.java
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package ai.vespa.hosted.client;
-
-import ai.vespa.hosted.client.ConfigServerClient.ResponseException;
-import ai.vespa.hosted.client.ConfigServerClient.HostStrategy;
-import com.github.tomakehurst.wiremock.http.Fault;
-import com.yahoo.vespa.athenz.api.AthenzService;
-import org.apache.hc.core5.http.Method;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
-
-import java.io.UncheckedIOException;
-import java.net.URI;
-import java.util.List;
-
-import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
-import static com.github.tomakehurst.wiremock.client.WireMock.anyRequestedFor;
-import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl;
-import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
-import static com.github.tomakehurst.wiremock.client.WireMock.get;
-import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor;
-import static com.github.tomakehurst.wiremock.client.WireMock.okJson;
-import static com.github.tomakehurst.wiremock.client.WireMock.post;
-import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor;
-import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
-import static java.nio.charset.StandardCharsets.UTF_8;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
-/**
- * @author jonmv
- */
-class HttpConfigServerClientTest {
-
- @RegisterExtension
- final WireMockExtension server = new WireMockExtension();
-
- final ConfigServerClient client = new HttpConfigServerClient(List.of(new AthenzService("mydomain", "yourservice")), "user");
-
- @Test
- void testRetries() {
- // Two servers in list--two attempts on IOException.
- server.stubFor(get("/root?query=foo"))
- .setResponse(okJson("{}").withFault(Fault.RANDOM_DATA_THEN_CLOSE)
- .build());
- assertThrows(UncheckedIOException.class,
- () -> client.send(HostStrategy.ordered(List.of(URI.create("http://localhost:" + server.port()),
- URI.create("http://localhost:" + server.port() + "/"))),
- Method.GET)
- .at("root")
- .parameters("query", "foo")
- .discard());
- server.verify(2, getRequestedFor(urlEqualTo("/root?query=foo")));
- server.verify(2, anyRequestedFor(anyUrl()));
- server.resetRequests();
-
- // Two attempts on a different IOException.
- server.stubFor(post("/prefix/root"))
- .setResponse(okJson("{}").withFault(Fault.EMPTY_RESPONSE)
- .build());
- assertThrows(UncheckedIOException.class,
- () -> client.send(HostStrategy.shuffling(List.of(URI.create("http://localhost:" + server.port() + "/prefix"),
- URI.create("http://localhost:" + server.port() + "/prefix/"))),
- Method.POST)
- .body("hello".getBytes(UTF_8))
- .at("root")
- .stream());
- server.verify(2, postRequestedFor(urlEqualTo("/prefix/root")).withRequestBody(equalTo("hello")));
- server.verify(2, anyRequestedFor(anyUrl()));
- server.resetRequests();
-
- // Successful attempt returns.
- server.stubFor(get("/root/boot/toot"))
- .setResponse(okJson("{}").build());
- assertEquals("{}",
- client.send(HostStrategy.repeating(URI.create("http://localhost:" + server.port()), 10),
- Method.GET)
- .at("root", "boot")
- .at("toot")
- .read(String::new));
- server.verify(1, getRequestedFor(urlEqualTo("/root/boot/toot")));
- server.verify(1, anyRequestedFor(anyUrl()));
- server.resetRequests();
-
- // ResponseException is not retried.
- server.stubFor(get("/"))
- .setResponse(aResponse().withStatus(409).withBody("hi").build());
- ResponseException thrown = assertThrows(ResponseException.class,
- () -> client.send(HostStrategy.repeating(URI.create("http://localhost:" + server.port()), 10),
- Method.GET)
- .read(String::new));
- assertEquals("GET http://localhost:" + server.port() + "/ failed with status 409 and body 'hi'", thrown.getMessage());
- server.verify(1, getRequestedFor(urlEqualTo("/")));
- server.verify(1, anyRequestedFor(anyUrl()));
- server.resetRequests();
- }
-
-}
diff --git a/configserver-client/src/test/java/ai/vespa/hosted/client/WireMockExtension.java b/configserver-client/src/test/java/ai/vespa/hosted/client/WireMockExtension.java
deleted file mode 100644
index d95650727c0..00000000000
--- a/configserver-client/src/test/java/ai/vespa/hosted/client/WireMockExtension.java
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package ai.vespa.hosted.client;
-
-import com.github.tomakehurst.wiremock.WireMockServer;
-import com.github.tomakehurst.wiremock.core.Options;
-import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
-import org.junit.jupiter.api.extension.AfterEachCallback;
-import org.junit.jupiter.api.extension.BeforeEachCallback;
-import org.junit.jupiter.api.extension.ExtensionContext;
-
-/**
- * Allows wiremock to be used as a JUnit 5 extension, like
- * <pre>
- *
- * &#64RegisterExtension
- * WireMockExtension mockServer1 = new WireMockExtension();
- * </pre>
- */
-public class WireMockExtension extends WireMockServer implements BeforeEachCallback, AfterEachCallback {
-
- public WireMockExtension() {
- this(WireMockConfiguration.options()
- .dynamicPort()
- .dynamicHttpsPort());
- }
-
- public WireMockExtension(Options options) {
- super(options);
- }
-
- @Override
- public void beforeEach(ExtensionContext extensionContext) {
- start();
- }
-
- @Override
- public void afterEach(ExtensionContext extensionContext) {
- stop();
- resetAll();
- }
-
-}