diff --git a/.circleci/config.yml b/.circleci/config.yml index 34322d23d..3ad66ad46 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -170,6 +170,9 @@ jobs: docker-img: <> topology: <> ssl: <> + - run: + name: Start proxy + command: ./docker/start_proxy.sh - load_cache - run: name: mvn dependency:tree @@ -235,6 +238,9 @@ jobs: docker-img: <> topology: <> ssl: <> + - run: + name: Start proxy + command: ./docker/start_proxy.sh - load_cache - install - run: diff --git a/docker/start_proxy.sh b/docker/start_proxy.sh new file mode 100755 index 000000000..b4e938684 --- /dev/null +++ b/docker/start_proxy.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +docker run -d \ + -e LOG_LEVEL=Info \ + -e AUTH_USER=user \ + -e AUTH_PASSWORD=password \ + --network=arangodb -p 8888:8888 \ + docker.io/kalaksi/tinyproxy:1.7 diff --git a/http-protocol/src/main/java/com/arangodb/http/HttpConnection.java b/http-protocol/src/main/java/com/arangodb/http/HttpConnection.java index bb3e0f441..87a47c760 100644 --- a/http-protocol/src/main/java/com/arangodb/http/HttpConnection.java +++ b/http-protocol/src/main/java/com/arangodb/http/HttpConnection.java @@ -87,7 +87,7 @@ private static String getUserAgent() { return "JavaDriver/" + PackageVersion.VERSION + " (JVM/" + System.getProperty("java.specification.version") + ")"; } - HttpConnection(final ArangoConfig config, final HostDescription host, final Vertx existingVertx) { + HttpConnection(final ArangoConfig config, final HostDescription host, final HttpProtocolConfig protocolConfig) { super(); Protocol protocol = config.getProtocol(); ContentType contentType = ContentTypeFactory.of(protocol); @@ -113,9 +113,9 @@ private static String getUserAgent() { ).toHttpAuthorization(); Vertx vertxToUse; - if (existingVertx != null) { + if (protocolConfig.getVertx() != null) { // reuse existing Vert.x - vertxToUse = existingVertx; + vertxToUse = protocolConfig.getVertx(); // Vert.x will not be closed when connection is closed vertxToClose = null; LOGGER.debug("Reusing existing Vert.x instance"); @@ -154,7 +154,8 @@ private static String getUserAgent() { .setHttp2ClearTextUpgrade(false) .setProtocolVersion(httpVersion) .setDefaultHost(host.getHost()) - .setDefaultPort(host.getPort()); + .setDefaultPort(host.getPort()) + .setProxyOptions(protocolConfig.getProxyOptions()); if (compression != Compression.NONE) { webClientOptions.setTryUseCompression(true); diff --git a/http-protocol/src/main/java/com/arangodb/http/HttpConnectionFactory.java b/http-protocol/src/main/java/com/arangodb/http/HttpConnectionFactory.java index edde3da19..69980aec3 100644 --- a/http-protocol/src/main/java/com/arangodb/http/HttpConnectionFactory.java +++ b/http-protocol/src/main/java/com/arangodb/http/HttpConnectionFactory.java @@ -34,12 +34,11 @@ public class HttpConnectionFactory implements ConnectionFactory { private final Logger LOGGER = LoggerFactory.getLogger(HttpConnectionFactory.class); - private final Vertx vertx; + final HttpProtocolConfig protocolConfig; - public HttpConnectionFactory(@UnstableApi final HttpProtocolConfig config) { - HttpProtocolConfig cfg = config != null ? config : HttpProtocolConfig.builder().build(); - vertx = cfg.getVertx(); - if (vertx == null && !PackageVersion.SHADED && Vertx.currentContext() != null) { + public HttpConnectionFactory(@UnstableApi final HttpProtocolConfig cfg) { + protocolConfig = cfg != null ? cfg : HttpProtocolConfig.builder().build(); + if (protocolConfig.getVertx() == null && !PackageVersion.SHADED && Vertx.currentContext() != null) { LOGGER.warn("Found an existing Vert.x instance, you can reuse it by setting:\n" + "new ArangoDB.Builder()\n" + " // ...\n" + @@ -51,6 +50,6 @@ public HttpConnectionFactory(@UnstableApi final HttpProtocolConfig config) { @Override @UnstableApi public Connection create(@UnstableApi final ArangoConfig config, final HostDescription host) { - return new HttpConnection(config, host, vertx); + return new HttpConnection(config, host, protocolConfig); } } diff --git a/http-protocol/src/main/java/com/arangodb/http/HttpProtocolConfig.java b/http-protocol/src/main/java/com/arangodb/http/HttpProtocolConfig.java index 26c47f825..7a62dc505 100644 --- a/http-protocol/src/main/java/com/arangodb/http/HttpProtocolConfig.java +++ b/http-protocol/src/main/java/com/arangodb/http/HttpProtocolConfig.java @@ -2,9 +2,11 @@ import com.arangodb.config.ProtocolConfig; import io.vertx.core.Vertx; +import io.vertx.core.net.ProxyOptions; public final class HttpProtocolConfig implements ProtocolConfig { private final Vertx vertx; + private final ProxyOptions proxyOptions; public static Builder builder() { return new Builder(); @@ -12,6 +14,7 @@ public static Builder builder() { public static class Builder { private Vertx vertx; + private ProxyOptions proxyOptions; private Builder() { } @@ -27,16 +30,30 @@ public Builder vertx(Vertx vertx) { return this; } + /** + * @param proxyOptions proxy options for HTTP connections + * @return this builder + */ + public Builder proxyOptions(ProxyOptions proxyOptions) { + this.proxyOptions = proxyOptions; + return this; + } + public HttpProtocolConfig build() { - return new HttpProtocolConfig(vertx); + return new HttpProtocolConfig(vertx, proxyOptions); } } - private HttpProtocolConfig(Vertx vertx) { + private HttpProtocolConfig(Vertx vertx, ProxyOptions proxyOptions) { this.vertx = vertx; + this.proxyOptions = proxyOptions; } public Vertx getVertx() { return vertx; } + + public ProxyOptions getProxyOptions() { + return proxyOptions; + } } diff --git a/test-functional/pom.xml b/test-functional/pom.xml index bb960521d..ebe379394 100644 --- a/test-functional/pom.xml +++ b/test-functional/pom.xml @@ -37,7 +37,8 @@ **/SerdeTest.**, **/SerializableTest.**, **/JacksonInterferenceTest.**, - **/JacksonRequestContextTest.** + **/JacksonRequestContextTest.**, + **/HttpProxyTest.** @@ -101,7 +102,19 @@ - src/test-ssl/java + src/test-ssl/java + + + + no-ssl + + + ssl + !true + + + + src/test/java diff --git a/test-functional/src/test-ssl/java/com/arangodb/HttpProxyTest.java b/test-functional/src/test-ssl/java/com/arangodb/HttpProxyTest.java new file mode 100644 index 000000000..32305f526 --- /dev/null +++ b/test-functional/src/test-ssl/java/com/arangodb/HttpProxyTest.java @@ -0,0 +1,103 @@ +/* + * DISCLAIMER + * + * Copyright 2016 ArangoDB GmbH, Cologne, Germany + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Copyright holder is ArangoDB GmbH, Cologne, Germany + */ + +package com.arangodb; + +import com.arangodb.entity.ArangoDBVersion; +import com.arangodb.http.HttpProtocolConfig; +import io.netty.handler.proxy.ProxyConnectException; +import io.vertx.core.net.ProxyOptions; +import io.vertx.core.net.ProxyType; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + + +/** + * @author Michele Rastelli + */ +class HttpProxyTest extends BaseTest { + + @ParameterizedTest + @EnumSource(Protocol.class) + void httpProxy(Protocol protocol) { + assumeTrue(protocol != Protocol.VST); + + final ArangoDB arangoDB = new ArangoDB.Builder() + .protocol(protocol) + .host("172.28.0.1", 8529) + .password("test") + .useSsl(true) + .sslContext(createSslContext()) + .verifyHost(false) + .protocolConfig(HttpProtocolConfig.builder() + .proxyOptions(new ProxyOptions() + .setType(ProxyType.HTTP) + .setHost("172.28.0.1") + .setPort(8888) + .setUsername("user") + .setPassword("password")) + .build()) + .build(); + final ArangoDBVersion version = arangoDB.getVersion(); + assertThat(version).isNotNull(); + } + + + @ParameterizedTest + @EnumSource(Protocol.class) + void httpProxyWrongPassword(Protocol protocol) { + assumeTrue(protocol != Protocol.VST); + + final ArangoDB arangoDB = new ArangoDB.Builder() + .protocol(protocol) + .host("172.28.0.1", 8529) + .password("test") + .useSsl(true) + .sslContext(createSslContext()) + .verifyHost(false) + .protocolConfig(HttpProtocolConfig.builder() + .proxyOptions(new ProxyOptions() + .setType(ProxyType.HTTP) + .setHost("172.28.0.1") + .setPort(8888) + .setUsername("user") + .setPassword("wrong")) + .build()) + .build(); + Throwable thrown = catchThrowable(arangoDB::getVersion); + assertThat(thrown) + .isInstanceOf(ArangoDBException.class) + .hasMessageContaining("Cannot contact any host!") + .cause() + .isInstanceOf(ArangoDBMultipleException.class); + List causes = ((ArangoDBMultipleException) thrown.getCause()).getExceptions(); + assertThat(causes).allSatisfy(e -> assertThat(e) + .isInstanceOf(ProxyConnectException.class) + .hasMessageContaining("status: 401 Unauthorized")); + assertThat(version).isNotNull(); + } + +} diff --git a/test-parent/pom.xml b/test-parent/pom.xml index b6e977667..aae41facc 100644 --- a/test-parent/pom.xml +++ b/test-parent/pom.xml @@ -20,6 +20,7 @@ true 17 17 + src/test/java @@ -153,7 +154,7 @@ - src/test/java + ${testSources} false @@ -209,7 +210,7 @@ - ${project.basedir}/src/test/java + ${project.basedir}/${testSources} ** ${project.build.directory}/generated-test-sources replacer