Skip to content

Commit de86cf4

Browse files
committed
experimenting with unix socket support using redis:// scheme
1 parent f3f4245 commit de86cf4

File tree

2 files changed

+61
-57
lines changed

2 files changed

+61
-57
lines changed

redis/conn.go

+55-55
Original file line numberDiff line numberDiff line change
@@ -324,10 +324,8 @@ func DialURL(rawurl string, options ...DialOption) (Conn, error) {
324324
}
325325

326326
// DialURLContext connects to a Redis server at the given URL using the Redis
327-
// URI scheme. It supports:
328-
// redis - unencrypted tcp connection
329-
// rediss - TLS encrypted tcp connection
330-
// redis+socket - UNIX socket connection
327+
// URI scheme. URLs should follow the draft IANA specification for the
328+
// scheme (https://www.iana.org/assignments/uri-schemes/prov/redis).
331329
func DialURLContext(ctx context.Context, rawurl string, options ...DialOption) (Conn, error) {
332330
u, err := url.Parse(rawurl)
333331
if err != nil {
@@ -344,66 +342,68 @@ func DialURLContext(ctx context.Context, rawurl string, options ...DialOption) (
344342
)
345343
switch u.Scheme {
346344
case "redis", "rediss":
347-
// As per the IANA draft spec, the host defaults to localhost and
348-
// the port defaults to 6379.
349-
host, port, err := net.SplitHostPort(u.Host)
350-
if err != nil {
351-
// assume port is missing
352-
host = u.Host
353-
port = "6379"
354-
}
355-
if host == "" {
356-
host = "localhost"
357-
}
358-
network = "tcp"
359-
address = net.JoinHostPort(host, port)
360-
361-
if u.User != nil {
362-
password, isSet := u.User.Password()
363-
username := u.User.Username()
364-
if isSet {
365-
if username != "" {
366-
// ACL
367-
options = append(options, DialUsername(username), DialPassword(password))
368-
} else {
369-
// requirepass - user-info username:password with blank username
370-
options = append(options, DialPassword(password))
371-
}
372-
} else if username != "" {
373-
// requirepass - redis-cli compatibility which treats as single arg in user-info as a password
374-
options = append(options, DialPassword(username))
375-
}
376-
}
377-
match := pathDBRegexp.FindStringSubmatch(u.Path)
378-
if len(match) == 2 {
379-
if len(match[1]) > 0 {
380-
db, err = strconv.Atoi(match[1])
345+
if (u.Host == "" || u.Host == ".") && len(u.Path) > 0 && u.Path[0] == '/' {
346+
network = "unix"
347+
348+
address = u.Path
349+
dbParameter := u.Query().Get("db")
350+
if dbParameter != "" {
351+
db, err = strconv.Atoi(dbParameter)
381352
if err != nil {
382353
return nil, fmt.Errorf("invalid database: %s", u.Path[1:])
383354
}
355+
if db != 0 {
356+
options = append(options, DialDatabase(db))
357+
}
384358
}
385-
if db != 0 {
386-
options = append(options, DialDatabase(db))
387-
}
388-
} else if u.Path != "" {
389-
return nil, fmt.Errorf("invalid database: %s", u.Path[1:])
390-
}
359+
} else {
360+
network = "tcp"
391361

392-
options = append(options, DialUseTLS(u.Scheme == "rediss"))
393-
case "redis+socket":
394-
network = "unix"
395-
address = u.Path
396-
dbParameter := u.Query().Get("db")
397-
if dbParameter != "" {
398-
db, err = strconv.Atoi(dbParameter)
362+
// As per the IANA draft spec, the host defaults to localhost and
363+
// the port defaults to 6379.
364+
host, port, err := net.SplitHostPort(u.Host)
399365
if err != nil {
400-
return nil, fmt.Errorf("invalid database: %s", u.Path[1:])
366+
// assume port is missing
367+
host = u.Host
368+
port = "6379"
369+
}
370+
if host == "" {
371+
host = "localhost"
401372
}
402-
if db != 0 {
403-
options = append(options, DialDatabase(db))
373+
address = net.JoinHostPort(host, port)
374+
if u.User != nil {
375+
password, isSet := u.User.Password()
376+
username := u.User.Username()
377+
if isSet {
378+
if username != "" {
379+
// ACL
380+
options = append(options, DialUsername(username), DialPassword(password))
381+
} else {
382+
// requirepass - user-info username:password with blank username
383+
options = append(options, DialPassword(password))
384+
}
385+
} else if username != "" {
386+
// requirepass - redis-cli compatibility which treats as single arg in user-info as a password
387+
options = append(options, DialPassword(username))
388+
}
389+
}
390+
match := pathDBRegexp.FindStringSubmatch(u.Path)
391+
if len(match) == 2 {
392+
if len(match[1]) > 0 {
393+
db, err = strconv.Atoi(match[1])
394+
if err != nil {
395+
return nil, fmt.Errorf("invalid database: %s", u.Path[1:])
396+
}
397+
}
398+
if db != 0 {
399+
options = append(options, DialDatabase(db))
400+
}
401+
} else if u.Path != "" {
402+
return nil, fmt.Errorf("invalid database: %s", u.Path[1:])
404403
}
405404
}
406-
options = append(options, DialUseTLS(false))
405+
406+
options = append(options, DialUseTLS(u.Scheme == "rediss"))
407407
default:
408408
return nil, fmt.Errorf("invalid redis URL scheme: %s", u.Scheme)
409409
}

redis/conn_test.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"math"
2525
"net"
2626
"os"
27+
"path/filepath"
2728
"reflect"
2829
"strings"
2930
"sync"
@@ -661,6 +662,8 @@ func TestDialURLHost(t *testing.T) {
661662
}
662663
}
663664

665+
var workingDirectory, _ = os.Getwd()
666+
664667
var dialURLTests = []struct {
665668
description string
666669
url string
@@ -680,8 +683,9 @@ var dialURLTests = []struct {
680683
{"database 3", "redis://localhost/3", "+OK\r\n", "*2\r\n$6\r\nSELECT\r\n$1\r\n3\r\n"},
681684
{"database 99", "redis://localhost/99", "+OK\r\n", "*2\r\n$6\r\nSELECT\r\n$2\r\n99\r\n"},
682685
{"no database", "redis://localhost/", "+OK\r\n", ""},
683-
{"database 99", "redis+socket://./server.sock?db=99", "+OK\r\n", "*2\r\n$6\r\nSELECT\r\n$2\r\n99\r\n"},
684-
{"no database", "redis+socket://./server.sock", "+OK\r\n", ""},
686+
{"absolute socket path", "redis://"+filepath.Join(workingDirectory, "server.sock"), "+OK\r\n", ""},
687+
{"relative socket path", "redis://./server.sock", "+OK\r\n", ""},
688+
{"socket path database 99", "redis://./server.sock?db=99", "+OK\r\n", "*2\r\n$6\r\nSELECT\r\n$2\r\n99\r\n"},
685689
}
686690

687691
func TestDialURL(t *testing.T) {

0 commit comments

Comments
 (0)