Skip to content

[DE-930] HTTP proxy support #584

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@ jobs:
docker-img: <<parameters.docker-img>>
topology: <<parameters.topology>>
ssl: <<parameters.ssl>>
- run:
name: Start proxy
command: ./docker/start_proxy.sh
- load_cache
- run:
name: mvn dependency:tree
Expand Down Expand Up @@ -235,6 +238,9 @@ jobs:
docker-img: <<parameters.docker-img>>
topology: <<parameters.topology>>
ssl: <<parameters.ssl>>
- run:
name: Start proxy
command: ./docker/start_proxy.sh
- load_cache
- install
- run:
Expand Down
8 changes: 8 additions & 0 deletions docker/start_proxy.sh
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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");
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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" +
Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@

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();
}

public static class Builder {
private Vertx vertx;
private ProxyOptions proxyOptions;

private Builder() {
}
Expand All @@ -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;
}
}
17 changes: 15 additions & 2 deletions test-functional/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@
**/SerdeTest.**,
**/SerializableTest.**,
**/JacksonInterferenceTest.**,
**/JacksonRequestContextTest.**
**/JacksonRequestContextTest.**,
**/HttpProxyTest.**
</filesToExclude>
<replacements>
<replacement>
Expand Down Expand Up @@ -101,7 +102,19 @@
</property>
</activation>
<properties>
<testSourceDirectory>src/test-ssl/java</testSourceDirectory>
<testSources>src/test-ssl/java</testSources>
</properties>
</profile>
<profile>
<id>no-ssl</id>
<activation>
<property>
<name>ssl</name>
<value>!true</value>
</property>
</activation>
<properties>
<testSources>src/test/java</testSources>
</properties>
</profile>
<profile>
Expand Down
103 changes: 103 additions & 0 deletions test-functional/src/test-ssl/java/com/arangodb/HttpProxyTest.java
Original file line number Diff line number Diff line change
@@ -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<Throwable> causes = ((ArangoDBMultipleException) thrown.getCause()).getExceptions();
assertThat(causes).allSatisfy(e -> assertThat(e)
.isInstanceOf(ProxyConnectException.class)
.hasMessageContaining("status: 401 Unauthorized"));
assertThat(version).isNotNull();
}

}
5 changes: 3 additions & 2 deletions test-parent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<maven.deploy.skip>true</maven.deploy.skip>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<testSources>src/test/java</testSources>
<shaded/>
</properties>

Expand Down Expand Up @@ -153,7 +154,7 @@
</activation>

<properties>
<testSourceDirectory>src/test/java</testSourceDirectory>
<testSourceDirectory>${testSources}</testSourceDirectory>
<shaded>false</shaded>
</properties>
<dependencies>
Expand Down Expand Up @@ -209,7 +210,7 @@
</execution>
</executions>
<configuration>
<basedir>${project.basedir}/src/test/java</basedir>
<basedir>${project.basedir}/${testSources}</basedir>
<filesToInclude>**</filesToInclude>
<outputBasedir>${project.build.directory}/generated-test-sources</outputBasedir>
<outputDir>replacer</outputDir>
Expand Down