Skip to content

Fix for bug #4003. Better message instead of ArrayIndexOutOfBoundsExce #4109

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
merged 8 commits into from
Mar 9, 2025
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
50 changes: 49 additions & 1 deletion src/main/java/redis/clients/jedis/util/JedisURIHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,32 @@
import redis.clients.jedis.Protocol;
import redis.clients.jedis.RedisProtocol;

/**
* Utility class for handling Redis URIs.
* This class provides methods to extract various components from a Redis URI,
* such as host, port, user, password, database index, and protocol.
* It also includes methods to validate the URI and check its scheme.
*
* <h2>URI syntax</h2>
*
* <blockquote>
* <i>redis[s]</i><b>{@code ://}</b>[[<i>username</i>]<i>[{@code :}password</i>]@]
* <i>host</i>[<b>{@code :}</b><i>port</i>][<b>{@code /}</b><i>database</i>]
* </blockquote>
*
*
* <h2>Authentication</h2>
* <p>Authentication details can be provided in the URI in the form of a username and password.
* Redis URIs may contain authentication details that effectively lead to usernames with passwords,
* password-only, or no authentication.</p>
* <h3>Examples:</h3>
* <ul>
* <li><b>Username and Password:</b> redis://username:password@host:port</li>
* <li><b>Password-only:</b> redis://:password@host:port</li>
* <li><b>Empty password:</b> redis://username:@host:port</li>
* <li><b>No Authentication:</b> redis://host:port</li>
* </ul>
*/
public final class JedisURIHelper {

private static final String REDIS = "redis";
Expand All @@ -18,6 +44,14 @@ public static HostAndPort getHostAndPort(URI uri) {
return new HostAndPort(uri.getHost(), uri.getPort());
}

/**
* Extracts the user from the given URI.
* <p>
* For details on the URI format and authentication examples, see {@link JedisURIHelper}.
* </p>
* @param uri the URI to extract the user from
* @return the user as a String, or null if user is empty or {@link URI#getUserInfo()} info is missing
*/
public static String getUser(URI uri) {
String userInfo = uri.getUserInfo();
if (userInfo != null) {
Expand All @@ -30,10 +64,24 @@ public static String getUser(URI uri) {
return null;
}

/**
* Extracts the password from the given URI.
* <p>
* For details on the URI format and authentication examples, see {@link JedisURIHelper}.
* </p>
* @param uri the URI to extract the password from
* @return the password as a String, or null if {@link URI#getUserInfo()} info is missing
* @throws IllegalArgumentException if {@link URI#getUserInfo()} is provided but does not contain
* a password
*/
public static String getPassword(URI uri) {
String userInfo = uri.getUserInfo();
if (userInfo != null) {
return userInfo.split(":", 2)[1];
String[] userAndPassword = userInfo.split(":", 2);
if (userAndPassword.length < 2) {
throw new IllegalArgumentException("Password not provided in uri.");
}
return userAndPassword[1];
}
return null;
}
Expand Down
36 changes: 29 additions & 7 deletions src/test/java/redis/clients/jedis/util/JedisURIHelperTest.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package redis.clients.jedis.util;

import static org.junit.Assert.*;
import static redis.clients.jedis.util.JedisURIHelper.*;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;

import java.net.URI;
import java.net.URISyntaxException;
import org.junit.Test;
import redis.clients.jedis.RedisProtocol;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.emptyString;

public class JedisURIHelperTest {

Expand Down Expand Up @@ -69,8 +68,31 @@ public void shouldGetDefaultProtocolWhenNotDefined() {
@Test
public void shouldGetProtocolFromDefinition() {
assertEquals(RedisProtocol.RESP3, getRedisProtocol(URI.create("redis://host:1234?protocol=3")));
assertEquals(RedisProtocol.RESP3, getRedisProtocol(URI.create("redis://host:1234/?protocol=3")));
assertEquals(RedisProtocol.RESP3, getRedisProtocol(URI.create("redis://host:1234/1?protocol=3")));
assertEquals(RedisProtocol.RESP3, getRedisProtocol(URI.create("redis://host:1234/1/?protocol=3")));
assertEquals(RedisProtocol.RESP3,
getRedisProtocol(URI.create("redis://host:1234/?protocol=3")));
assertEquals(RedisProtocol.RESP3,
getRedisProtocol(URI.create("redis://host:1234/1?protocol=3")));
assertEquals(RedisProtocol.RESP3,
getRedisProtocol(URI.create("redis://host:1234/1/?protocol=3")));
}

@Test
public void emptyPassword() {
// ensure we can provide an empty password for default user
assertThat(JedisURIHelper.getPassword(URI.create("redis://:@host:9000/0")), emptyString());

// ensure we can provide an empty password for user
assertEquals(JedisURIHelper.getUser(URI.create("redis://username:@host:9000/0")), "username");
assertThat(JedisURIHelper.getPassword(URI.create("redis://username:@host:9000/0")),
emptyString());
}

@Test
public void shouldThrowIfNoPasswordInURI() throws URISyntaxException {
// ensure we throw if user is provided but password is missing in URI
URI uri = new URI("redis://user@host:9000/0");
IllegalArgumentException illegalArgumentException = assertThrows(IllegalArgumentException.class,
() -> getPassword(uri));
assertEquals("Password not provided in uri.", illegalArgumentException.getMessage());
}
}
Loading