-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Expose method to add upstream driver libraries to CLIENT SETINFO payload #3542
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
viktoriya-kutsarova
merged 4 commits into
redis:main
from
viktoriya-kutsarova:expose-methods-to-identify-upstream-drivers
Dec 4, 2025
+485
−16
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
cfa5e3e
Expose method to add upstream driver libraries to CLIENT SETINFO payload
viktoriya-kutsarova 3b6b3d4
Create a separate class to hold driver name and upstream drivers info…
viktoriya-kutsarova 91f1f8c
Fix PR comments
viktoriya-kutsarova 289e840
Update since tag
viktoriya-kutsarova File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,248 @@ | ||
| /* | ||
| * Copyright 2025, Redis Ltd. and Contributors | ||
| * All rights reserved. | ||
| * | ||
| * Licensed under the MIT License. | ||
| */ | ||
|
|
||
| package io.lettuce.core; | ||
|
|
||
| import io.lettuce.core.internal.LettuceAssert; | ||
| import io.lettuce.core.internal.LettuceSets; | ||
|
|
||
| import java.io.Serializable; | ||
| import java.util.ArrayList; | ||
| import java.util.Collections; | ||
| import java.util.List; | ||
| import java.util.Set; | ||
|
|
||
| /** | ||
| * Immutable class representing driver information for Redis client identification. | ||
| * <p> | ||
| * This class is used to identify the client library and any upstream drivers (such as Spring Data Redis or Spring Session) when | ||
| * connecting to Redis. The information is sent via the {@code CLIENT SETINFO} command. | ||
| * <p> | ||
| * The formatted name follows the pattern: {@code name(driver1_vVersion1;driver2_vVersion2)} | ||
| * | ||
| * @author Viktoriya Kutsarova | ||
| * @since 7.2 | ||
| * @see RedisURI#setDriverInfo(DriverInfo) | ||
| * @see RedisURI#getDriverInfo() | ||
| * @see <a href="https://redis.io/docs/latest/commands/client-setinfo/">CLIENT SETINFO</a> | ||
| */ | ||
| public final class DriverInfo implements Serializable { | ||
|
|
||
| /** | ||
| * Regex pattern for driver name validation. The name must start with a lowercase letter and contain only lowercase letters, | ||
| * digits, hyphens, and underscores. Mostly follows Maven artifactId naming conventions but also allows underscores. | ||
| * | ||
| * @see <a href="https://maven.apache.org/guides/mini/guide-naming-conventions.html">Maven Naming Conventions</a> | ||
| */ | ||
| private static final String DRIVER_NAME_PATTERN = "^[a-z][a-z0-9_-]*$"; | ||
|
|
||
| /** | ||
| * Set of brace characters that are not allowed in driver names or versions. These characters are used to delimit the driver | ||
| * information in the formatted output and would break parsing. | ||
| */ | ||
| private static final Set<Character> BRACES = LettuceSets.unmodifiableSet('(', ')', '[', ']', '{', '}'); | ||
|
|
||
| private final String name; | ||
|
|
||
| private final List<String> upstreamDrivers; | ||
|
|
||
| private DriverInfo(String name, List<String> upstreamDrivers) { | ||
| this.name = name; | ||
| this.upstreamDrivers = Collections.unmodifiableList(upstreamDrivers); | ||
| } | ||
|
|
||
| /** | ||
| * Creates a new {@link Builder} with default values. | ||
| * <p> | ||
| * The default name is "Lettuce" (from {@link LettuceVersion#getName()}). | ||
| * | ||
| * @return a new builder instance | ||
| */ | ||
| public static Builder builder() { | ||
| return new Builder(); | ||
| } | ||
|
|
||
| /** | ||
| * Creates a new {@link Builder} initialized with values from an existing {@link DriverInfo}. | ||
| * | ||
| * @param driverInfo the existing driver info to copy from, must not be {@code null} | ||
| * @return a new builder instance initialized with the existing values | ||
| * @throws IllegalArgumentException if driverInfo is {@code null} | ||
| */ | ||
| public static Builder builder(DriverInfo driverInfo) { | ||
| LettuceAssert.notNull(driverInfo, "DriverInfo must not be null"); | ||
| return new Builder(driverInfo); | ||
| } | ||
|
|
||
| /** | ||
| * Returns the formatted name including upstream drivers. | ||
| * <p> | ||
| * If no upstream drivers are present, returns just the name. Otherwise, returns the name followed by upstream drivers in | ||
| * parentheses, separated by semicolons. | ||
| * <p> | ||
| * Examples: | ||
| * <ul> | ||
| * <li>{@code "Lettuce"} - no upstream drivers</li> | ||
| * <li>{@code "Lettuce(spring-data-redis_v3.2.0)"} - one upstream driver</li> | ||
| * <li>{@code "Lettuce(spring-session_v3.3.0;spring-data-redis_v3.2.0)"} - multiple upstream drivers</li> | ||
| * </ul> | ||
| * | ||
| * @return the formatted name for use in CLIENT SETINFO | ||
| */ | ||
| public String getFormattedName() { | ||
| if (upstreamDrivers.isEmpty()) { | ||
| return name; | ||
| } | ||
| return String.format("%s(%s)", name, String.join(";", upstreamDrivers)); | ||
| } | ||
|
|
||
| /** | ||
| * Returns the base library name without upstream driver information. | ||
| * | ||
| * @return the library name | ||
| */ | ||
| public String getName() { | ||
| return name; | ||
| } | ||
|
|
||
| @Override | ||
| public String toString() { | ||
| return getFormattedName(); | ||
| } | ||
|
|
||
| /** | ||
| * Builder for creating {@link DriverInfo} instances. | ||
| */ | ||
| public static class Builder { | ||
|
|
||
| private String name; | ||
|
|
||
| private final List<String> upstreamDrivers; | ||
|
|
||
| private Builder() { | ||
| this.name = LettuceVersion.getName(); | ||
| this.upstreamDrivers = new ArrayList<>(); | ||
| } | ||
|
|
||
| private Builder(DriverInfo driverInfo) { | ||
| this.name = driverInfo.name; | ||
| this.upstreamDrivers = new ArrayList<>(driverInfo.upstreamDrivers); | ||
| } | ||
|
|
||
| /** | ||
| * Sets the base library name. | ||
| * <p> | ||
| * This overrides the default name ("Lettuce"). Use this when you want to completely customise the library | ||
| * identification. | ||
| * | ||
| * @param name the library name, must not be {@code null} | ||
| * @return this builder | ||
| * @throws IllegalArgumentException if name is {@code null} | ||
| */ | ||
| public Builder name(String name) { | ||
| LettuceAssert.notNull(name, "Name must not be null"); | ||
| this.name = name; | ||
| return this; | ||
| } | ||
|
|
||
| /** | ||
| * Adds an upstream driver to the driver information. | ||
| * <p> | ||
| * Upstream drivers are prepended to the list, so the most recently added driver appears first in the formatted output. | ||
| * <p> | ||
| * The driver name must follow Maven artifactId naming conventions: lowercase letters, digits, hyphens, and underscores | ||
| * only, starting with a lowercase letter. | ||
| * <p> | ||
| * Both values must not contain spaces, newlines, non-printable characters, or brace characters as these would violate | ||
| * the format of the Redis CLIENT LIST reply. | ||
| * | ||
| * @param driverName the name of the upstream driver (e.g., "spring-data-redis"), must not be {@code null} | ||
| * @param driverVersion the version of the upstream driver (e.g., "3.2.0"), must not be {@code null} | ||
| * @return this builder | ||
| * @throws IllegalArgumentException if the driver name or version is {@code null} or has invalid format | ||
| * @see <a href="https://maven.apache.org/guides/mini/guide-naming-conventions.html">Maven Naming Conventions</a> | ||
| * @see <a href="https://redis.io/docs/latest/commands/client-setinfo/">CLIENT SETINFO</a> | ||
| */ | ||
| public Builder addUpstreamDriver(String driverName, String driverVersion) { | ||
| LettuceAssert.notNull(driverName, "Driver name must not be null"); | ||
| LettuceAssert.notNull(driverVersion, "Driver version must not be null"); | ||
| validateDriverName(driverName); | ||
| validateDriverVersion(driverVersion); | ||
| String formattedDriverInfo = formatDriverInfo(driverName, driverVersion); | ||
| this.upstreamDrivers.add(0, formattedDriverInfo); | ||
| return this; | ||
| } | ||
|
|
||
| /** | ||
| * Builds and returns a new immutable {@link DriverInfo} instance. | ||
| * | ||
| * @return a new DriverInfo instance | ||
| */ | ||
| public DriverInfo build() { | ||
| return new DriverInfo(name, upstreamDrivers); | ||
| } | ||
|
|
||
| } | ||
|
|
||
| /** | ||
| * Validates that the driver name follows Maven artifactId naming conventions: lowercase letters, digits, hyphens, and | ||
| * underscores only, starting with a lowercase letter (e.g., {@code spring-data-redis}, {@code lettuce-core}, | ||
| * {@code akka-redis_2.13}). | ||
| * <p> | ||
| * Additionally validates Redis CLIENT LIST constraints: no spaces, newlines, non-printable characters, or braces. | ||
| * | ||
| * @param driverName the driver name to validate | ||
| * @throws IllegalArgumentException if the driver name does not follow the expected naming conventions | ||
| * @see <a href="https://maven.apache.org/guides/mini/guide-naming-conventions.html">Maven Naming Conventions</a> | ||
| * @see <a href="https://redis.io/docs/latest/commands/client-setinfo/">CLIENT SETINFO</a> | ||
| */ | ||
| private static void validateDriverName(String driverName) { | ||
| validateNoInvalidCharacters(driverName, "Driver name"); | ||
| if (!driverName.matches(DRIVER_NAME_PATTERN)) { | ||
| throw new IllegalArgumentException( | ||
| "Upstream driver name must follow Maven artifactId naming conventions: lowercase letters, digits, hyphens, and underscores only (e.g., 'spring-data-redis', 'lettuce-core')"); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Validates that the driver version does not contain characters that would violate the format of the Redis CLIENT LIST | ||
| * reply: spaces, newlines, non-printable characters, or brace characters. | ||
| * | ||
| * @param driverVersion the driver version to validate | ||
| * @throws IllegalArgumentException if the driver version contains invalid characters | ||
| * @see <a href="https://redis.io/docs/latest/commands/client-setinfo/">CLIENT SETINFO</a> | ||
| */ | ||
| private static void validateDriverVersion(String driverVersion) { | ||
| validateNoInvalidCharacters(driverVersion, "Driver version"); | ||
| } | ||
|
|
||
| /** | ||
| * Validates that the value does not contain characters that would violate the format of the Redis CLIENT LIST reply: | ||
| * non-printable characters, spaces, or brace characters. | ||
| * <p> | ||
| * Only printable ASCII characters (0x21-0x7E, i.e., '!' to '~') are allowed, excluding braces. | ||
| * | ||
| * @param value the value to validate | ||
| * @param fieldName the name of the field for error messages | ||
| * @throws IllegalArgumentException if the value contains invalid characters | ||
| * @see <a href="https://redis.io/docs/latest/commands/client-setinfo/">CLIENT SETINFO</a> | ||
| */ | ||
| private static void validateNoInvalidCharacters(String value, String fieldName) { | ||
| for (int i = 0; i < value.length(); i++) { | ||
| char c = value.charAt(i); | ||
| if (c < '!' || c > '~' || BRACES.contains(c)) { | ||
| throw new IllegalArgumentException( | ||
| fieldName + " must not contain spaces, newlines, non-printable characters, or braces"); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private static String formatDriverInfo(String driverName, String driverVersion) { | ||
| return driverName + "_v" + driverVersion; | ||
| } | ||
|
|
||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.