@@ -225,6 +225,24 @@ public class RedisURI implements Serializable, ConnectionPoint {
225225
226226 public static final Duration DEFAULT_TIMEOUT_DURATION = Duration .ofSeconds (DEFAULT_TIMEOUT );
227227
228+ /**
229+ * Regex pattern for driver name validation. The name must start with a lowercase letter and contain only lowercase letters,
230+ * digits, hyphens, and underscores. Mostly follows Maven artifactId naming conventions but also allows underscores.
231+ *
232+ * @see <a href="https://maven.apache.org/guides/mini/guide-naming-conventions.html">Maven Naming Conventions</a>
233+ */
234+ private static final String DRIVER_NAME_PATTERN = "^[a-z][a-z0-9_-]*$" ;
235+
236+ /**
237+ * Official semver.org regex pattern for semantic versioning validation.
238+ *
239+ * @see <a href="https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string">semver.org
240+ * regex</a>
241+ */
242+ private static final String SEMVER_PATTERN = "^(0|[1-9]\\ d*)\\ .(0|[1-9]\\ d*)\\ .(0|[1-9]\\ d*)"
243+ + "(?:-((?:0|[1-9]\\ d*|\\ d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\ .(?:0|[1-9]\\ d*|\\ d*[a-zA-Z-][0-9a-zA-Z-]*))*))?"
244+ + "(?:\\ +([0-9a-zA-Z-]+(?:\\ .[0-9a-zA-Z-]+)*))?$" ;
245+
228246 private String host ;
229247
230248 private String socket ;
@@ -239,9 +257,11 @@ public class RedisURI implements Serializable, ConnectionPoint {
239257
240258 private String libraryName = LettuceVersion .getName ();
241259
260+ private String upstreamDrivers ;
261+
242262 private String libraryVersion = LettuceVersion .getVersion ();
243263
244- private RedisCredentialsProvider credentialsProvider = new StaticCredentialsProvider (null , null );;
264+ private RedisCredentialsProvider credentialsProvider = new StaticCredentialsProvider (null , null );
245265
246266 private boolean ssl = false ;
247267
@@ -606,13 +626,19 @@ public void setClientName(String clientName) {
606626 }
607627
608628 /**
609- * Returns the library name.
629+ * Returns the library name to be sent via {@code CLIENT SETINFO}.
630+ * <p>
631+ * If upstream drivers have been added via {@link #addUpstreamDriver(String, String)}, the returned value will include them
632+ * in the format: {@code libraryName(driver1_v1.0.0;driver2_v2.0.0)}. Otherwise, returns just the library name.
610633 *
611- * @return the library name.
634+ * @return the library name, potentially including upstream driver information .
612635 * @since 6.3
613636 */
614637 public String getLibraryName () {
615- return libraryName ;
638+ if (upstreamDrivers == null ) {
639+ return libraryName ;
640+ }
641+ return libraryName + "(" + upstreamDrivers + ")" ;
616642 }
617643
618644 /**
@@ -628,6 +654,79 @@ public void setLibraryName(String libraryName) {
628654 this .libraryName = libraryName ;
629655 }
630656
657+ /**
658+ * Adds an upstream driver to be appended to the library name when sent via {@code CLIENT SETINFO}.
659+ * <p>
660+ * This method allows upstream libraries (e.g., Spring Data Redis) that use Lettuce as their Redis driver to identify
661+ * themselves. Multiple upstream drivers can be added and will be formatted according to the Redis CLIENT SETINFO format:
662+ * {@code lettuce(driver1_v1.0.0;driver2_v2.0.0)}.
663+ * <p>
664+ * Each newly added upstream driver is prepended to the list, so the most recently added driver appears first. For example,
665+ * if you call {@code addUpstreamDriver("spring-data-redis", "3.2.0")} followed by
666+ * {@code addUpstreamDriver("spring-boot", "3.3.0")}, the resulting library name will be
667+ * {@code lettuce(spring-boot_v3.3.0;spring-data-redis_v3.2.0)}.
668+ * <p>
669+ * The driver name must follow <a href="https://maven.apache.org/guides/mini/guide-naming-conventions.html">Maven artifactId
670+ * naming conventions</a>: lowercase letters, digits, hyphens, and underscores only, starting with a lowercase letter (e.g.,
671+ * {@code spring-data-redis}, {@code lettuce-core}, {@code akka-redis_2.13}).
672+ * <p>
673+ * The driver version must follow <a href="https://semver.org/">semantic versioning</a> (e.g., {@code 1.0.0},
674+ * {@code 2.1.3-beta}, {@code 1.0.0-alpha.1}, {@code 1.0.0+build.123}).
675+ *
676+ * @param driverName the name of the upstream driver (e.g., "spring-data-redis"), must not be {@code null} and must follow
677+ * <a href="https://maven.apache.org/guides/mini/guide-naming-conventions.html">Maven artifactId naming
678+ * conventions</a>
679+ * @param driverVersion the version of the upstream driver (e.g., "3.2.0"), must not be {@code null} and must follow
680+ * <a href="https://semver.org/">semantic versioning</a>
681+ * @throws IllegalArgumentException if the driver name or version format is invalid
682+ * @since 6.5
683+ * @see <a href="https://redis.io/docs/latest/commands/client-setinfo/">CLIENT SETINFO</a>
684+ */
685+ public void addUpstreamDriver (String driverName , String driverVersion ) {
686+
687+ LettuceAssert .notNull (driverName , "Upstream driver name must not be null" );
688+ LettuceAssert .notNull (driverVersion , "Upstream driver version must not be null" );
689+ validateDriverName (driverName );
690+ validateDriverVersion (driverVersion );
691+
692+ String driver = driverName + "_v" + driverVersion ;
693+ this .upstreamDrivers = this .upstreamDrivers == null ? driver : driver + ";" + this .upstreamDrivers ;
694+ }
695+
696+ /**
697+ * Validates that the driver name follows Maven artifactId naming conventions: lowercase letters, digits, hyphens, and
698+ * underscores only, starting with a lowercase letter (e.g., {@code spring-data-redis}, {@code lettuce-core},
699+ * {@code akka-redis_2.13}).
700+ *
701+ * @param driverName the driver name to validate
702+ * @throws IllegalArgumentException if the driver name does not follow the expected naming conventions
703+ * @see <a href="https://maven.apache.org/guides/mini/guide-naming-conventions.html">Maven Naming Conventions</a>
704+ */
705+ private static void validateDriverName (String driverName ) {
706+ if (!driverName .matches (DRIVER_NAME_PATTERN )) {
707+ throw new IllegalArgumentException (
708+ "Upstream driver name must follow Maven artifactId naming conventions: lowercase letters, digits, hyphens, and underscores only (e.g., 'spring-data-redis', 'lettuce-core')" );
709+ }
710+ }
711+
712+ /**
713+ * Validates that the driver version follows semantic versioning (semver.org). The version must be in the format
714+ * {@code MAJOR.MINOR.PATCH} with optional pre-release and build metadata suffixes.
715+ * <p>
716+ * Examples of valid versions: {@code 1.0.0}, {@code 2.1.3}, {@code 1.0.0-alpha}, {@code 1.0.0-alpha.1},
717+ * {@code 1.0.0-0.3.7}, {@code 1.0.0-x.7.z.92}, {@code 1.0.0+20130313144700}, {@code 1.0.0-beta+exp.sha.5114f85}
718+ *
719+ * @param driverVersion the driver version to validate
720+ * @throws IllegalArgumentException if the driver version does not follow semantic versioning
721+ * @see <a href="https://semver.org/">Semantic Versioning 2.0.0</a>
722+ */
723+ private static void validateDriverVersion (String driverVersion ) {
724+ if (!driverVersion .matches (SEMVER_PATTERN )) {
725+ throw new IllegalArgumentException (
726+ "Upstream driver version must follow semantic versioning (e.g., '1.0.0', '2.1.3-beta', '1.0.0+build.123')" );
727+ }
728+ }
729+
631730 /**
632731 * Returns the library version.
633732 *
@@ -1295,6 +1394,8 @@ public static class Builder {
12951394
12961395 private String libraryVersion = LettuceVersion .getVersion ();
12971396
1397+ private String upstreamDrivers ;
1398+
12981399 private RedisCredentialsProvider credentialsProvider ;
12991400
13001401 private boolean ssl = false ;
@@ -1630,6 +1731,50 @@ public Builder withLibraryName(String libraryName) {
16301731 return this ;
16311732 }
16321733
1734+ /**
1735+ * Adds an upstream driver to be appended to the library name when sent via {@code CLIENT SETINFO}.
1736+ * <p>
1737+ * This method allows upstream libraries (e.g., Spring Data Redis) that use Lettuce as their Redis driver to identify
1738+ * themselves. Multiple upstream drivers can be added and will be formatted according to the Redis CLIENT SETINFO
1739+ * format: {@code lettuce(driver1_v1.0.0;driver2_v2.0.0)}.
1740+ * <p>
1741+ * Each newly added upstream driver is prepended to the list, so the most recently added driver appears first. For
1742+ * example, if you call {@code addUpstreamDriver("spring-data-redis", "3.2.0")} followed by
1743+ * {@code addUpstreamDriver("spring-boot", "3.3.0")}, the resulting library name will be
1744+ * {@code lettuce(spring-boot_v3.3.0;spring-data-redis_v3.2.0)}.
1745+ * <p>
1746+ * The driver name must follow <a href="https://maven.apache.org/guides/mini/guide-naming-conventions.html">Maven
1747+ * artifactId naming conventions</a>: lowercase letters, digits, hyphens, and underscores only, starting with a
1748+ * lowercase letter (e.g., {@code spring-data-redis}, {@code lettuce-core}, {@code akka-redis_2.13}).
1749+ * <p>
1750+ * The driver version must follow <a href="https://semver.org/">semantic versioning</a> (e.g., {@code 1.0.0},
1751+ * {@code 2.1.3-beta}, {@code 1.0.0-alpha.1}, {@code 1.0.0+build.123}).
1752+ * <p>
1753+ * Also sets upstream driver for already configured Redis Sentinel nodes.
1754+ *
1755+ * @param driverName the name of the upstream driver (e.g., "spring-data-redis"), must not be {@code null} and must
1756+ * follow <a href="https://maven.apache.org/guides/mini/guide-naming-conventions.html">Maven artifactId naming
1757+ * conventions</a>
1758+ * @param driverVersion the version of the upstream driver (e.g., "3.2.0"), must not be {@code null} and must follow
1759+ * <a href="https://semver.org/">semantic versioning</a>
1760+ * @return the builder
1761+ * @throws IllegalArgumentException if the driver name or version format is invalid
1762+ * @since 6.5
1763+ * @see <a href="https://redis.io/docs/latest/commands/client-setinfo/">CLIENT SETINFO</a>
1764+ */
1765+ public Builder addUpstreamDriver (String driverName , String driverVersion ) {
1766+
1767+ LettuceAssert .notNull (driverName , "Upstream driver name must not be null" );
1768+ LettuceAssert .notNull (driverVersion , "Upstream driver version must not be null" );
1769+ validateDriverName (driverName );
1770+ validateDriverVersion (driverVersion );
1771+
1772+ String driver = driverName + "_v" + driverVersion ;
1773+ this .upstreamDrivers = this .upstreamDrivers == null ? driver : driver + ";" + this .upstreamDrivers ;
1774+ this .sentinels .forEach (it -> it .addUpstreamDriver (driverName , driverVersion ));
1775+ return this ;
1776+ }
1777+
16331778 /**
16341779 * Configures a library version. Sets library version also for already configured Redis Sentinel nodes.
16351780 *
@@ -1790,6 +1935,7 @@ public RedisURI build() {
17901935 redisURI .setClientName (clientName );
17911936 redisURI .setLibraryName (libraryName );
17921937 redisURI .setLibraryVersion (libraryVersion );
1938+ redisURI .upstreamDrivers = upstreamDrivers ;
17931939
17941940 redisURI .setSentinelMasterId (sentinelMasterId );
17951941
0 commit comments