From f40caaca196b86e6e1ac6d232aa0847a1bf2945a Mon Sep 17 00:00:00 2001 From: Christopher Broglie Date: Fri, 27 Jun 2025 01:35:53 -0700 Subject: [PATCH] Fix Redis Sentinel URI parsing This fixes an issue in lettucemod's RedisURIBuilder helper in v4.2.0 by forking it into this repo. The issue is fixed in lettucemod v4.3.0, and the fork can be deleted when upgrading (deferred as it's a breaking change affecting other dependencies as well). Fixes #175 --- .../java/com/redis/riot/RedisContext.java | 1 - .../java/com/redis/riot/RedisURIBuilder.java | 155 ++++++++++++++++++ .../java/com/redis/riot/RedisArgsTests.java | 13 ++ 3 files changed, 168 insertions(+), 1 deletion(-) create mode 100644 plugins/riot/src/main/java/com/redis/riot/RedisURIBuilder.java diff --git a/plugins/riot/src/main/java/com/redis/riot/RedisContext.java b/plugins/riot/src/main/java/com/redis/riot/RedisContext.java index 78515d0d..0ddbe675 100644 --- a/plugins/riot/src/main/java/com/redis/riot/RedisContext.java +++ b/plugins/riot/src/main/java/com/redis/riot/RedisContext.java @@ -4,7 +4,6 @@ import com.redis.lettucemod.RedisModulesClientBuilder; import com.redis.lettucemod.RedisModulesUtils; -import com.redis.lettucemod.RedisURIBuilder; import com.redis.lettucemod.api.StatefulRedisModulesConnection; import com.redis.spring.batch.item.redis.RedisItemReader; import com.redis.spring.batch.item.redis.RedisItemWriter; diff --git a/plugins/riot/src/main/java/com/redis/riot/RedisURIBuilder.java b/plugins/riot/src/main/java/com/redis/riot/RedisURIBuilder.java new file mode 100644 index 00000000..a43e0f68 --- /dev/null +++ b/plugins/riot/src/main/java/com/redis/riot/RedisURIBuilder.java @@ -0,0 +1,155 @@ +// Copied from https://github.com/redis/lettucemod/blob/v4.2.0/core/lettucemod/src/main/java/com/redis/lettucemod/RedisURIBuilder.java +// and modified to fix https://github.com/redis/riot/issues/175. +package com.redis.riot; + +import java.time.Duration; + +import com.redis.lettucemod.RedisModulesUtils; + +import io.lettuce.core.RedisURI; +import io.lettuce.core.SslVerifyMode; +import io.lettuce.core.internal.LettuceAssert; +import io.lettuce.core.internal.LettuceStrings; +import lombok.ToString; + +@ToString +public class RedisURIBuilder { + + public static final String DEFAULT_HOST = "127.0.0.1"; + public static final int DEFAULT_PORT = RedisURI.DEFAULT_REDIS_PORT; + public static final Duration DEFAULT_TIMEOUT_DURATION = RedisURI.DEFAULT_TIMEOUT_DURATION; + public static final long DEFAULT_TIMEOUT = RedisURI.DEFAULT_TIMEOUT; + public static final SslVerifyMode DEFAULT_VERIFY_MODE = SslVerifyMode.FULL; + + private RedisURI uri; + private String host = DEFAULT_HOST; + private int port = DEFAULT_PORT; + private String socket; + private String username; + private char[] password; + private Duration timeout = DEFAULT_TIMEOUT_DURATION; + private int database; + private String clientName; + private String libraryName; + private String libraryVersion; + private boolean tls; + private SslVerifyMode verifyMode = DEFAULT_VERIFY_MODE; + + public RedisURI build() { + RedisURI.Builder builder = redisURIBuilder(); + if (!RedisModulesUtils.isEmpty(password)) { + if (RedisModulesUtils.hasLength(username)) { + builder.withAuthentication(username, password); + } else { + builder.withPassword(password); + } + } + if (database > 0) { + builder.withDatabase(database); + } + if (tls) { + builder.withSsl(tls); + } + builder.withVerifyPeer(verifyMode); + if (timeout != null) { + builder.withTimeout(timeout); + } + RedisURI redisURI = builder.build(); + if (RedisModulesUtils.hasLength(libraryName) && !RedisModulesUtils.hasLength(redisURI.getLibraryName())) { + redisURI.setLibraryName(libraryName); + } + if (RedisModulesUtils.hasLength(libraryVersion) && !RedisModulesUtils.hasLength(redisURI.getLibraryVersion())) { + redisURI.setLibraryVersion(libraryVersion); + } + if (RedisModulesUtils.hasLength(clientName) && !RedisModulesUtils.hasLength(redisURI.getClientName())) { + redisURI.setClientName(clientName); + } + return redisURI; + } + + private RedisURI.Builder redisURIBuilder() { + if (uri != null) { + RedisURI.Builder builder = RedisURI.builder(uri); + if (LettuceStrings.isNotEmpty(uri.getSentinelMasterId())) { + builder.withSentinelMasterId(uri.getSentinelMasterId()); + } + uri.getSentinels().forEach(builder::withSentinel); + return builder; + } + if (RedisModulesUtils.hasLength(socket)) { + return RedisURI.Builder.socket(socket); + } + return RedisURI.Builder.redis(host, port); + } + + public RedisURIBuilder uri(RedisURI uri) { + this.uri = uri; + return this; + } + + public RedisURIBuilder host(String host) { + this.host = host; + return this; + } + + public RedisURIBuilder port(int port) { + this.port = port; + return this; + } + + public RedisURIBuilder socket(String socket) { + this.socket = socket; + return this; + } + + public RedisURIBuilder username(String username) { + this.username = username; + return this; + } + + public RedisURIBuilder password(String password) { + LettuceAssert.notNull(password, "Password must not be null"); + return password(password.toCharArray()); + } + + public RedisURIBuilder password(char[] password) { + this.password = password; + return this; + } + + public RedisURIBuilder timeout(Duration timeout) { + this.timeout = timeout; + return this; + } + + public RedisURIBuilder database(int database) { + this.database = database; + return this; + } + + public RedisURIBuilder clientName(String clientName) { + this.clientName = clientName; + return this; + } + + public RedisURIBuilder libraryName(String libraryName) { + this.libraryName = libraryName; + return this; + } + + public RedisURIBuilder libraryVersion(String libraryVersion) { + this.libraryVersion = libraryVersion; + return this; + } + + public RedisURIBuilder tls(boolean tls) { + this.tls = tls; + return this; + } + + public RedisURIBuilder verifyMode(SslVerifyMode verifyMode) { + this.verifyMode = verifyMode; + return this; + } + +} diff --git a/plugins/riot/src/test/java/com/redis/riot/RedisArgsTests.java b/plugins/riot/src/test/java/com/redis/riot/RedisArgsTests.java index c3f528df..889e41a6 100644 --- a/plugins/riot/src/test/java/com/redis/riot/RedisArgsTests.java +++ b/plugins/riot/src/test/java/com/redis/riot/RedisArgsTests.java @@ -1,5 +1,7 @@ package com.redis.riot; +import java.util.List; + import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -20,6 +22,17 @@ void redisArgsURI() { Assertions.assertEquals(args.getClientName(), uri.getClientName()); } + @Test + void redisArgsSentinelURI() { + RedisArgs args = new RedisArgs(); + RedisURI uri = RedisURI.create("redis-sentinel://host1:26379,host2:26380/?sentinelMasterId=foo"); + args.setUri(uri); + RedisContext context = RedisContext.of(args); + Assertions.assertEquals("foo", context.getUri().getSentinelMasterId()); + List sentinels = context.getUri().getSentinels(); + Assertions.assertEquals(2, sentinels.size()); + } + @Test void parseRedisURI() { String host = "blah";