diff --git a/db-client-java/src/main/java/com/eventstore/dbclient/ConnectionSettingsBuilder.java b/db-client-java/src/main/java/com/eventstore/dbclient/ConnectionSettingsBuilder.java index 8ea7c00c..baa92482 100644 --- a/db-client-java/src/main/java/com/eventstore/dbclient/ConnectionSettingsBuilder.java +++ b/db-client-java/src/main/java/com/eventstore/dbclient/ConnectionSettingsBuilder.java @@ -1,6 +1,5 @@ package com.eventstore.dbclient; - import io.grpc.ClientInterceptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -15,6 +14,7 @@ * Utility to create client settings programmatically. */ public class ConnectionSettingsBuilder { + private static final Logger logger = LoggerFactory.getLogger(ConnectionSettingsBuilder.class); private boolean _dnsDiscover = false; private int _maxDiscoverAttempts = 3; @@ -33,10 +33,12 @@ public class ConnectionSettingsBuilder { private String _tlsCaFile = null; private Set _features = new HashSet<>(); - ConnectionSettingsBuilder() {} + ConnectionSettingsBuilder() { + } /** * Returns configured connection settings. + * * @see EventStoreDBClientSettings * @return configured settings. */ @@ -76,7 +78,8 @@ public ConnectionSettingsBuilder maxDiscoverAttempts(int maxDiscoverAttempts) { } /** - * How long to wait before retrying a new discovery process (in milliseconds). + * How long to wait before retrying a new discovery process (in + * milliseconds). */ public ConnectionSettingsBuilder discoveryInterval(int discoveryInterval) { this._discoveryInterval = discoveryInterval; @@ -163,15 +166,17 @@ public ConnectionSettingsBuilder addHost(InetSocketAddress host) { } /** - * The amount of time (in milliseconds) the sender of the keepalive ping waits for an acknowledgement. + * The amount of time (in milliseconds) the sender of the keepalive ping + * waits for an acknowledgement. */ public ConnectionSettingsBuilder keepAliveTimeout(long value) { if (value >= 0 && value < Consts.DEFAULT_KEEP_ALIVE_TIMEOUT_IN_MS) { logger.warn("Specified keepAliveTimeout of {} is less than recommended {}", value, Consts.DEFAULT_KEEP_ALIVE_TIMEOUT_IN_MS); } - if (value == -1) + if (value == -1) { value = Long.MAX_VALUE; + } this._keepAliveTimeout = value; @@ -179,15 +184,17 @@ public ConnectionSettingsBuilder keepAliveTimeout(long value) { } /** - * The amount of time (in milliseconds) to wait after which a keepalive ping is sent on the transport. + * The amount of time (in milliseconds) to wait after which a keepalive ping + * is sent on the transport. */ public ConnectionSettingsBuilder keepAliveInterval(long value) { if (value >= 0 && value < Consts.DEFAULT_KEEP_ALIVE_INTERVAL_IN_MS) { logger.warn("Specified keepAliveInterval of {} is less than recommended {}", value, Consts.DEFAULT_KEEP_ALIVE_INTERVAL_IN_MS); } - if (value == -1) + if (value == -1) { value = Long.MAX_VALUE; + } this._keepAliveInterval = value; @@ -204,6 +211,7 @@ public ConnectionSettingsBuilder defaultDeadline(long value) { /** * Register a gRPC interceptor every time a new gRPC channel is created. + * * @param interceptor */ public ConnectionSettingsBuilder addInterceptor(ClientInterceptor interceptor) { @@ -212,8 +220,10 @@ public ConnectionSettingsBuilder addInterceptor(ClientInterceptor interceptor) { } /** - * Client certificate for secure connection. Not required for enabling secure connection. Useful for self-signed - * certificate that are not installed on the system trust store. + * Client certificate for secure connection. Not required for enabling + * secure connection. Useful for self-signed certificate that are not + * installed on the system trust store. + * * @param filepath path to a certificate file. */ public ConnectionSettingsBuilder tlsCaFile(String filepath) { @@ -246,7 +256,11 @@ void parseGossipSeed(String host) { break; case 2: try { - addHost(hostParts[0], Short.parseShort(hostParts[1])); + int port = Integer.parseInt(hostParts[1]); + if (port > 65535) { + throw new RuntimeException(new IllegalArgumentException(String.format("Invalid port number format: %s. Post cannot be higher than 65535.", hostParts[1]))); + } + addHost(hostParts[0], port); } catch (NumberFormatException e) { throw new RuntimeException(String.format("Invalid port number format: %s", hostParts[1])); } @@ -257,8 +271,9 @@ void parseGossipSeed(String host) { } static EventStoreDBClientSettings parseFromUrl(ConnectionSettingsBuilder builder, URL url) { - if (!url.getProtocol().equals("esdb") && !url.getProtocol().equals("esdb+discover")) + if (!url.getProtocol().equals("esdb") && !url.getProtocol().equals("esdb+discover")) { throw new RuntimeException(String.format("Unknown URL scheme: %s", url.getProtocol())); + } builder.dnsDiscover(url.getProtocol().equals("esdb+discover")); @@ -271,16 +286,18 @@ static EventStoreDBClientSettings parseFromUrl(ConnectionSettingsBuilder builder } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } - } - else + } else { builder.defaultCredentials(splits[0], ""); + } } - if (builder._hosts.isEmpty() && !url.getPath().isEmpty() && !url.getPath().equals("/")) + if (builder._hosts.isEmpty() && !url.getPath().isEmpty() && !url.getPath().equals("/")) { throw new RuntimeException(String.format("Unsupported URL path: %s", url.getPath())); + } - if (builder._hosts.isEmpty() && url.getHost().isEmpty()) + if (builder._hosts.isEmpty() && url.getHost().isEmpty()) { throw new RuntimeException("Connection string doesn't have an host"); + } if (builder._hosts.isEmpty()) { if (!url.getHost().contains(",")) { @@ -292,16 +309,18 @@ static EventStoreDBClientSettings parseFromUrl(ConnectionSettingsBuilder builder } } - if (url.getQuery() == null) + if (url.getQuery() == null) { return builder.buildConnectionSettings(); + } String userCertFile = null; String userKeyFile = null; for (String param : url.getQuery().split("&")) { String[] entry = param.split("="); - if (entry.length <= 1) + if (entry.length <= 1) { continue; + } String value = entry[1].toLowerCase(); switch (entry[0].toLowerCase()) { @@ -328,8 +347,9 @@ static EventStoreDBClientSettings parseFromUrl(ConnectionSettingsBuilder builder try { int parsedValue = Integer.parseInt(value); - if (parsedValue < 0) + if (parsedValue < 0) { invalidParamFormat(entry[0], value); + } builder._maxDiscoverAttempts = parsedValue; } catch (NumberFormatException e) { @@ -341,8 +361,9 @@ static EventStoreDBClientSettings parseFromUrl(ConnectionSettingsBuilder builder try { int parsedValue = Integer.parseInt(value); - if (parsedValue < 0) + if (parsedValue < 0) { invalidParamFormat(entry[0], value); + } builder._discoveryInterval = parsedValue; } catch (NumberFormatException e) { @@ -354,8 +375,9 @@ static EventStoreDBClientSettings parseFromUrl(ConnectionSettingsBuilder builder try { int parsedValue = Integer.parseInt(value); - if (parsedValue < 0) + if (parsedValue < 0) { invalidParamFormat(entry[0], value); + } builder._gossipTimeout = parsedValue; } catch (NumberFormatException e) { @@ -364,22 +386,25 @@ static EventStoreDBClientSettings parseFromUrl(ConnectionSettingsBuilder builder break; case "dnsdiscover": - if (!value.equals("true") && !value.equals("false")) + if (!value.equals("true") && !value.equals("false")) { invalidParamFormat(entry[0], value); + } builder._dnsDiscover = value.equals("true"); break; case "tls": - if (!value.equals("true") && !value.equals("false")) + if (!value.equals("true") && !value.equals("false")) { invalidParamFormat(entry[0], value); + } builder._tls = value.equals("true"); break; case "tlsverifycert": - if (!value.equals("true") && !value.equals("false")) + if (!value.equals("true") && !value.equals("false")) { invalidParamFormat(entry[0], value); + } builder._tlsVerifyCert = value.equals("true"); break; @@ -387,14 +412,17 @@ static EventStoreDBClientSettings parseFromUrl(ConnectionSettingsBuilder builder case "keepalivetimeout": try { long parsedValue = Long.parseLong(value); - if (parsedValue >= 0 && parsedValue < Consts.DEFAULT_KEEP_ALIVE_TIMEOUT_IN_MS) + if (parsedValue >= 0 && parsedValue < Consts.DEFAULT_KEEP_ALIVE_TIMEOUT_IN_MS) { logger.warn("Specified keepAliveTimeout of {} is less than recommended {}", parsedValue, Consts.DEFAULT_KEEP_ALIVE_TIMEOUT_IN_MS); + } - if (parsedValue < -1) + if (parsedValue < -1) { invalidParamFormat(entry[0], value); + } - if (parsedValue == -1) + if (parsedValue == -1) { parsedValue = Long.MAX_VALUE; + } builder._keepAliveTimeout = parsedValue; } catch (NumberFormatException e) { @@ -405,14 +433,17 @@ static EventStoreDBClientSettings parseFromUrl(ConnectionSettingsBuilder builder case "keepaliveinterval": try { long parsedValue = Long.parseLong(value); - if (parsedValue >= 0 && parsedValue < Consts.DEFAULT_KEEP_ALIVE_INTERVAL_IN_MS) + if (parsedValue >= 0 && parsedValue < Consts.DEFAULT_KEEP_ALIVE_INTERVAL_IN_MS) { logger.warn("Specified keepAliveInterval of {} is less than recommended {}", parsedValue, Consts.DEFAULT_KEEP_ALIVE_INTERVAL_IN_MS); + } - if (parsedValue < -1) + if (parsedValue < -1) { invalidParamFormat(entry[0], value); + } - if (parsedValue == -1) + if (parsedValue == -1) { parsedValue = Long.MAX_VALUE; + } builder._keepAliveInterval = parsedValue; } catch (NumberFormatException e) { @@ -424,8 +455,9 @@ static EventStoreDBClientSettings parseFromUrl(ConnectionSettingsBuilder builder try { long parsedValue = Long.parseLong(value); - if (parsedValue <= 0) + if (parsedValue <= 0) { invalidParamFormat(entry[0], value); + } builder._defaultDeadline = parsedValue; } catch (NumberFormatException e) { @@ -434,22 +466,25 @@ static EventStoreDBClientSettings parseFromUrl(ConnectionSettingsBuilder builder break; case "tlscafile": - if (entry[1].isEmpty()) + if (entry[1].isEmpty()) { invalidParamFormat(entry[0], entry[1]); + } builder._tlsCaFile = entry[1]; break; case "usercertfile": - if (entry[1].isEmpty()) + if (entry[1].isEmpty()) { invalidParamFormat(entry[0], entry[1]); + } userCertFile = entry[1]; break; case "userkeyfile": - if (entry[1].isEmpty()) + if (entry[1].isEmpty()) { invalidParamFormat(entry[0], entry[1]); + } userKeyFile = entry[1]; break; @@ -464,11 +499,13 @@ static EventStoreDBClientSettings parseFromUrl(ConnectionSettingsBuilder builder } } - if (userCertFile != null ^ userKeyFile != null) + if (userCertFile != null ^ userKeyFile != null) { throw new RuntimeException("Invalid user certificate settings. Both 'userCertFile' and 'userKeyFile' must be provided."); + } - if (userCertFile != null) + if (userCertFile != null) { builder.defaultClientCertificate(userCertFile, userKeyFile); + } return builder.buildConnectionSettings(); } diff --git a/db-client-java/src/main/java/com/eventstore/dbclient/resolution/FixedSeedsNodeResolution.java b/db-client-java/src/main/java/com/eventstore/dbclient/resolution/FixedSeedsNodeResolution.java index 858d316f..6d4f344f 100644 --- a/db-client-java/src/main/java/com/eventstore/dbclient/resolution/FixedSeedsNodeResolution.java +++ b/db-client-java/src/main/java/com/eventstore/dbclient/resolution/FixedSeedsNodeResolution.java @@ -1,6 +1,7 @@ package com.eventstore.dbclient.resolution; import java.net.InetSocketAddress; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -13,6 +14,6 @@ public FixedSeedsNodeResolution(InetSocketAddress[] seeds) { @Override public List resolve() { - return Arrays.asList(seeds); + return new ArrayList<>(Arrays.asList(seeds)); } } diff --git a/db-client-java/src/test/java/com/eventstore/dbclient/misc/ParseInvalidConnectionStringTests.java b/db-client-java/src/test/java/com/eventstore/dbclient/misc/ParseInvalidConnectionStringTests.java index f0fcb566..3f79bfcf 100644 --- a/db-client-java/src/test/java/com/eventstore/dbclient/misc/ParseInvalidConnectionStringTests.java +++ b/db-client-java/src/test/java/com/eventstore/dbclient/misc/ParseInvalidConnectionStringTests.java @@ -2,6 +2,7 @@ import com.eventstore.dbclient.ConnectionStringParsingException; import com.eventstore.dbclient.EventStoreDBConnectionString; + import org.junit.jupiter.api.Assertions; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -9,6 +10,7 @@ import java.util.stream.Stream; + public class ParseInvalidConnectionStringTests { public static Stream invalidConnectionStrings() { return Stream.of( @@ -27,7 +29,8 @@ public static Stream invalidConnectionStrings() { Arguments.of("esdb://localhost?keepAliveTimeout=-3"), Arguments.of("esdb://localhost?nodePreference=read_only_replica"), Arguments.of("esdb://localhost?userCertFile=/path/to/cert"), - Arguments.of("esdb://localhost?userKeyFile=/path/to/key") + Arguments.of("esdb://localhost?userKeyFile=/path/to/key"), + Arguments.of("esdb://localhost:65536,localhost:2113") ); } diff --git a/db-client-java/src/test/java/com/eventstore/dbclient/misc/ParseValidConnectionStringTests.java b/db-client-java/src/test/java/com/eventstore/dbclient/misc/ParseValidConnectionStringTests.java index 9a5ed76e..427a1da8 100644 --- a/db-client-java/src/test/java/com/eventstore/dbclient/misc/ParseValidConnectionStringTests.java +++ b/db-client-java/src/test/java/com/eventstore/dbclient/misc/ParseValidConnectionStringTests.java @@ -8,6 +8,8 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.json.JsonMapper; + +import org.junit.Test; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -234,4 +236,12 @@ private EventStoreDBClientSettings parseJson(String input) throws JsonProcessing return builder.buildConnectionSettings(); } + + @Test + public void testPortNumbers() { + //Test gossip seeds with port numbers higher than 32000. + EventStoreDBClientSettings settings = EventStoreDBConnectionString.parseOrThrow("esdb://localhost:4500,localhost:50000?feature=foobar&feature=baz"); + Assertions.assertNotNull(settings); + Assertions.assertEquals(2,settings.getHosts().length); + } }