Repository: gomodule/redigo
Pull request #566 (2021-06-09 19:06:01)

fix: DialURL compatibility with redis-cli
Fix compatibility of DialURL with respect for single component user-info records.

This enables URLs such as redis://mypass@localhost/1 as supported by redis-cli to be used.


Patch:
diff --git a/redis/conn.go b/redis/conn.go
index 1398b4d..7d757dc 100644
--- a/redis/conn.go
+++ b/redis/conn.go
@@ -168,6 +168,7 @@ func DialPassword(password string) DialOption {
 
 // DialUsername specifies the username to use when connecting to
 // the Redis server when Redis ACLs are used.
+// A DialPassword must also be passed otherwise this option will have no effect.
 func DialUsername(username string) DialOption {
 	return DialOption{func(do *dialOptions) {
 		do.username = username
@@ -347,8 +348,18 @@ func DialURL(rawurl string, options ...DialOption) (Conn, error) {
 
 	if u.User != nil {
 		password, isSet := u.User.Password()
+		username := u.User.Username()
 		if isSet {
-			options = append(options, DialUsername(u.User.Username()), DialPassword(password))
+			if username != "" {
+				// ACL
+				options = append(options, DialUsername(username), DialPassword(password))
+			} else {
+				// requirepass - user-info username:password with blank username
+				options = append(options, DialPassword(password))
+			}
+		} else if username != "" {
+			// requirepass - redis-cli compatibility which treats as single arg in user-info as a password
+			options = append(options, DialPassword(username))
 		}
 	}
 


Test patch:
diff --git a/redis/conn_test.go b/redis/conn_test.go
index 425c4f0..b33cc41 100644
--- a/redis/conn_test.go
+++ b/redis/conn_test.go
@@ -640,6 +640,13 @@ var dialURLTests = []struct {
 	w           string
 }{
 	{"password", "redis://:abc123@localhost", "+OK\r\n", "*2\r\n$4\r\nAUTH\r\n$6\r\nabc123\r\n"},
+	{"password redis-cli compat", "redis://abc123@localhost", "+OK\r\n", "*2\r\n$4\r\nAUTH\r\n$6\r\nabc123\r\n"},
+	{"password db1", "redis://:abc123@localhost/1", "+OK\r\n+OK\r\n", "*2\r\n$4\r\nAUTH\r\n$6\r\nabc123\r\n*2\r\n$6\r\nSELECT\r\n$1\r\n1\r\n"},
+	{"password db1 redis-cli compat", "redis://abc123@localhost/1", "+OK\r\n+OK\r\n", "*2\r\n$4\r\nAUTH\r\n$6\r\nabc123\r\n*2\r\n$6\r\nSELECT\r\n$1\r\n1\r\n"},
+	{"password no host db0", "redis://:abc123@/0", "+OK\r\n+OK\r\n", "*2\r\n$4\r\nAUTH\r\n$6\r\nabc123\r\n"},
+	{"password no host db0 redis-cli compat", "redis://abc123@/0", "+OK\r\n+OK\r\n", "*2\r\n$4\r\nAUTH\r\n$6\r\nabc123\r\n"},
+	{"password no host db1", "redis://:abc123@/1", "+OK\r\n+OK\r\n", "*2\r\n$4\r\nAUTH\r\n$6\r\nabc123\r\n*2\r\n$6\r\nSELECT\r\n$1\r\n1\r\n"},
+	{"password no host db1 redis-cli compat", "redis://abc123@/1", "+OK\r\n+OK\r\n", "*2\r\n$4\r\nAUTH\r\n$6\r\nabc123\r\n*2\r\n$6\r\nSELECT\r\n$1\r\n1\r\n"},
 	{"username and password", "redis://user:password@localhost", "+OK\r\n", "*3\r\n$4\r\nAUTH\r\n$4\r\nuser\r\n$8\r\npassword\r\n"},
 	{"username", "redis://x:@localhost", "+OK\r\n", ""},
 	{"database 3", "redis://localhost/3", "+OK\r\n", "*2\r\n$6\r\nSELECT\r\n$1\r\n3\r\n"},
@@ -649,16 +656,18 @@ var dialURLTests = []struct {
 
 func TestDialURL(t *testing.T) {
 	for _, tt := range dialURLTests {
-		var buf bytes.Buffer
-		// UseTLS should be ignored in all of these tests.
-		_, err := redis.DialURL(tt.url, dialTestConn(tt.r, &buf), redis.DialUseTLS(true))
-		if err != nil {
-			t.Errorf("%s dial error: %v", tt.description, err)
-			continue
-		}
-		if w := buf.String(); w != tt.w {
-			t.Errorf("%s commands = %q, want %q", tt.description, w, tt.w)
-		}
+		t.Run(tt.description, func(t *testing.T) {
+			var buf bytes.Buffer
+			// UseTLS should be ignored in all of these tests.
+			_, err := redis.DialURL(tt.url, dialTestConn(tt.r, &buf), redis.DialUseTLS(true))
+			if err != nil {
+				t.Errorf("%s dial error: %v, buf: %v", tt.description, err, buf.String())
+				return
+			}
+			if w := buf.String(); w != tt.w {
+				t.Errorf("%s commands = %q, want %q", tt.description, w, tt.w)
+			}
+		})
 	}
 }