Add unix socket connection by url stirng. (#1511)

* add unix connectin by url string
This commit is contained in:
Roman Suvorov 2020-09-28 15:26:08 +03:00 committed by GitHub
parent 3e42145a26
commit d31cf6c328
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 81 additions and 10 deletions

View File

@ -29,6 +29,7 @@ type Limiter interface {
ReportResult(result error)
}
// Options keeps the settings to setup redis connection.
type Options struct {
// The network type, either tcp or unix.
// Default is tcp.
@ -187,23 +188,32 @@ func (opt *Options) clone() *Options {
}
// ParseURL parses an URL into Options that can be used to connect to Redis.
// Scheme is required.
// There are two connection types: by tcp socket and by unix socket.
// Tcp connection:
// redis://<user>:<password>@<host>:<port>/<db_number>
// Unix connection:
// unix://<user>:<password>@</path/to/redis.sock>?db=<db_number>
func ParseURL(redisURL string) (*Options, error) {
o := &Options{Network: "tcp"}
u, err := url.Parse(redisURL)
if err != nil {
return nil, err
}
if u.Scheme != "redis" && u.Scheme != "rediss" {
return nil, errors.New("invalid redis URL scheme: " + u.Scheme)
switch u.Scheme {
case "redis", "rediss":
return setupTCPConn(u)
case "unix":
return setupUnixConn(u)
default:
return nil, fmt.Errorf("invalid redis URL scheme: %s", u.Scheme)
}
}
if u.User != nil {
o.Username = u.User.Username()
if p, ok := u.User.Password(); ok {
o.Password = p
}
}
func setupTCPConn(u *url.URL) (*Options, error) {
o := &Options{Network: "tcp"}
o.Username, o.Password = getUserPassword(u)
if len(u.Query()) > 0 {
return nil, errors.New("no options supported")
@ -232,15 +242,52 @@ func ParseURL(redisURL string) (*Options, error) {
return nil, fmt.Errorf("invalid redis database number: %q", f[0])
}
default:
return nil, errors.New("invalid redis URL path: " + u.Path)
return nil, fmt.Errorf("invalid redis URL path: %s", u.Path)
}
if u.Scheme == "rediss" {
o.TLSConfig = &tls.Config{ServerName: h}
}
return o, nil
}
func setupUnixConn(u *url.URL) (*Options, error) {
o := &Options{
Network: "unix",
}
if strings.TrimSpace(u.Path) == "" { // path is required with unix connection
return nil, errors.New("empty redis unix socket path")
}
o.Addr = u.Path
o.Username, o.Password = getUserPassword(u)
dbStr := u.Query().Get("db")
if dbStr == "" {
return o, nil // if database is not set, connect to 0 db.
}
db, err := strconv.Atoi(dbStr)
if err != nil {
return nil, fmt.Errorf("invalid reids database number: %s", err)
}
o.DB = db
return o, nil
}
func getUserPassword(u *url.URL) (string, string) {
var user, password string
if u.User != nil {
user = u.User.Username()
if p, ok := u.User.Password(); ok {
password = p
}
}
return user, password
}
func newConnPool(opt *Options) *pool.ConnPool {
return pool.NewConnPool(&pool.Options{
Dialer: func(ctx context.Context) (net.Conn, error) {

View File

@ -66,6 +66,30 @@ func TestParseURL(t *testing.T) {
0, false, nil,
"foo", "bar",
},
{
"unix:///tmp/redis.sock",
"/tmp/redis.sock",
0, false, nil,
"", "",
},
{
"unix://foo:bar@/tmp/redis.sock",
"/tmp/redis.sock",
0, false, nil,
"foo", "bar",
},
{
"unix://foo:bar@/tmp/redis.sock?db=3",
"/tmp/redis.sock",
3, false, nil,
"foo", "bar",
},
{
"unix://foo:bar@/tmp/redis.sock?db=test",
"/tmp/redis.sock",
0, false, errors.New("invalid reids database number: strconv.Atoi: parsing \"test\": invalid syntax"),
"", "",
},
{
"redis://localhost/?abc=123",
"",