Skip to content
This repository has been archived by the owner on Apr 5, 2024. It is now read-only.

[474] Add proxy options to http client to use Ethsigner behind proxy #475

Merged
merged 7 commits into from
Nov 23, 2022
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
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,37 @@ public void setDownstreamHttpPath(final String path) {
defaultValue = "localhost,127.0.0.1")
private final AllowListHostsProperty metricsHostAllowList = new AllowListHostsProperty();

@SuppressWarnings("FieldMayBeFinal") // Because PicoCLI requires Strings to not be final.
@Option(
names = {"--downstream-http-proxy-host"},
description = "Hostname for proxy connect, no proxy if null (default: null)",
jframe marked this conversation as resolved.
Show resolved Hide resolved
paramLabel = HOST_FORMAT_HELP,
arity = "1")
private String httpProxyHost = null;

@Option(
names = {"--downstream-http-proxy-port"},
paramLabel = PORT_FORMAT_HELP,
description = "Port for proxy connect (default: 80)",
arity = "1")
private final Integer httpProxyPort = 80;

@SuppressWarnings("FieldMayBeFinal") // Because PicoCLI requires Strings to not be final.
@Option(
names = {"--downstream-http-proxy-username"},
paramLabel = "<username>",
description = "Username for proxy connect, no authentication if null (default: null)",
arity = "1")
private String httpProxyUsername = null;

@SuppressWarnings("FieldMayBeFinal") // Because PicoCLI requires Strings to not be final.
@Option(
names = {"--downstream-http-proxy-password"},
paramLabel = "<password>",
description = "Password for proxy connect, no authentication if null (default: null)",
arity = "1")
private String httpProxyPassword = null;

@Override
public Level getLogLevel() {
return logLevel;
Expand Down Expand Up @@ -306,6 +337,26 @@ public List<String> getMetricsHostAllowList() {
return metricsHostAllowList;
}

@Override
public String getHttpProxyHost() {
return httpProxyHost;
}

@Override
public Integer getHttpProxyPort() {
return httpProxyPort;
}

@Override
public String getHttpProxyUsername() {
return httpProxyUsername;
}

@Override
public String getHttpProxyPassword() {
return httpProxyPassword;
}

@Override
public void run() {
// validation is performed to simulate similar behavior as with ArgGroups.
Expand All @@ -329,6 +380,10 @@ public String toString() {
.add("dataPath", dataPath)
.add("clientTlsOptions", clientTlsOptions)
.add("corsAllowedOrigins", rpcHttpCorsAllowedOrigins)
.add("httpProxyHost", httpProxyHost)
.add("httpProxyPort", httpProxyPort)
.add("httpProxyUsername", httpProxyUsername)
.add("httpProxyPassword", httpProxyPassword)
.toString();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@ public static Map<String, Object> baseCommandOptions() {
optionsMap.put("downstream-http-tls-keystore-password-file", "./test.pass");
optionsMap.put("downstream-http-tls-ca-auth-enabled", Boolean.FALSE);
optionsMap.put("downstream-http-tls-known-servers-file", "./test.txt");

optionsMap.put("downstream-http-proxy-host", "localhost");
optionsMap.put("downstream-http-proxy-port", 80);
optionsMap.put("downstream-http-proxy-username", "username");
optionsMap.put("downstream-http-proxy-password", "passwd");
return optionsMap;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ void fullyPopulatedCommandLineParsesIntoVariables(final boolean useConfigFile) {
assertThat(tlsClientConstaints.getKnownClientsFile())
.isEqualTo(Optional.of(new File("./known_clients")));
assertThat(tlsClientConstaints.isCaAuthorizedClientAllowed()).isTrue();
assertThat(config.getHttpProxyHost()).isEqualTo("localhost");
assertThat(config.getHttpProxyPort()).isEqualTo(80);
assertThat(config.getHttpProxyUsername()).isEqualTo("username");
assertThat(config.getHttpProxyPassword()).isEqualTo("passwd");

final Optional<ClientTlsOptions> downstreamTlsOptionsOptional = config.getClientTlsOptions();
assertThat(downstreamTlsOptionsOptional.isPresent()).isTrue();
Expand Down Expand Up @@ -152,6 +156,19 @@ void nonIntegerInputForDownstreamPortShowsError(final boolean useConfigFile) {
assertThat(commandOutput.toString()).containsOnlyOnce(defaultUsageText);
}

@ParameterizedTest
@ValueSource(booleans = {false, true})
void nonIntegerInputForHttpProxyPortShowsError(final boolean useConfigFile) {
final Map<String, Object> options = modifyOptionValue("downstream-http-proxy-port", "abc");
final List<String> args =
useConfigFile ? toConfigFileOptionsList(tempDir, options) : toOptionsList(options);
final boolean result = parser.parseCommandLine(args.toArray(String[]::new));
assertThat(result).isFalse();
assertThat(commandError.toString())
.contains("--downstream-http-proxy-port", "'abc' is not an int");
assertThat(commandOutput.toString()).containsOnlyOnce(defaultUsageText);
}

@ParameterizedTest
@ValueSource(booleans = {false, true})
void missingRequiredParamShowsAppropriateError(final boolean useConfigFile) {
Expand Down Expand Up @@ -202,6 +219,30 @@ void missingListenHostDefaultsToLoopback() {
"http-listen-host", config::getHttpListenHost, "127.0.0.1");
}

@Test
void missingHttpProxyHostDefaultsToNull() {
missingOptionalParameterIsValidAndMeetsDefault(
"downstream-http-proxy-host", config::getHttpProxyHost, null);
}

@Test
void missingHttpProxyPortDefaultsToLoopback() {
missingOptionalParameterIsValidAndMeetsDefault(
"downstream-http-proxy-port", config::getHttpProxyPort, 80);
}

@Test
void missingHttpProxyUsernameDefaultsToNull() {
missingOptionalParameterIsValidAndMeetsDefault(
"downstream-http-proxy-username", config::getHttpProxyUsername, null);
}

@Test
void missingHttpProxyPasswordDefaultsToNull() {
missingOptionalParameterIsValidAndMeetsDefault(
"downstream-http-proxy-password", config::getHttpProxyPassword, null);
}

@Test
void illegalSubCommandDisplaysErrorMessage() {
// NOTE: all required params must be specified
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,38 @@
import java.util.Optional;

import io.vertx.core.net.PfxOptions;
import io.vertx.core.net.ProxyOptions;
import io.vertx.ext.web.client.WebClientOptions;

class WebClientOptionsFactory {

public WebClientOptions createWebClientOptions(final Config config) {
final WebClientOptions clientOptions =
new WebClientOptions()
.setDefaultPort(config.getDownstreamHttpPort())
.setDefaultHost(config.getDownstreamHttpHost())
.setTryUseCompression(true);
.setTryUseCompression(true)
.setProxyOptions(getProxyOptions(config).orElse(null));

applyTlsOptions(clientOptions, config);
return clientOptions;
}

private static Optional<ProxyOptions> getProxyOptions(final Config config) {
final String proxyHost = config.getHttpProxyHost();
if (proxyHost == null) {
return Optional.empty();
}
final int proxyPort = config.getHttpProxyPort();
final ProxyOptions proxyOptions = new ProxyOptions().setHost(proxyHost).setPort(proxyPort);
final String proxyUsername = config.getHttpProxyUsername();
final String proxyPassword = config.getHttpProxyPassword();
if (proxyUsername != null && proxyPassword != null) {
proxyOptions.setUsername(proxyUsername).setPassword(proxyPassword);
}
return Optional.of(proxyOptions);
}

private void applyTlsOptions(final WebClientOptions webClientOptions, final Config config) {
final Optional<ClientTlsOptions> optionalClientTlsOptions = config.getClientTlsOptions();
if (optionalClientTlsOptions.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,12 @@ public interface Config {
Set<MetricCategory> getMetricCategories();

List<String> getMetricsHostAllowList();

String getHttpProxyHost();

Integer getHttpProxyPort();

String getHttpProxyUsername();

String getHttpProxyPassword();
}