mirror of
https://github.com/redis/go-redis.git
synced 2025-04-19 07:22:17 +03:00
wip
This commit is contained in:
parent
8fadbef84a
commit
5410adb9dc
39
auth/auth.go
Normal file
39
auth/auth.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
type StreamingCredentialsProvider interface {
|
||||||
|
// Subscribe subscribes to the credentials provider and returns a channel that will receive updates.
|
||||||
|
// The first response is blocking, then data will be pushed to the channel.
|
||||||
|
Subscribe(listener CredentialsListener) (Credentials, CancelProviderFunc, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CancelProviderFunc func() error
|
||||||
|
|
||||||
|
type CredentialsListener interface {
|
||||||
|
OnNext(credentials Credentials)
|
||||||
|
OnError(err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Credentials interface {
|
||||||
|
BasicAuth() (username string, password string)
|
||||||
|
RawCredentials() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type basicAuth struct {
|
||||||
|
username string
|
||||||
|
password string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *basicAuth) RawCredentials() string {
|
||||||
|
return b.username + ":" + b.password
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *basicAuth) BasicAuth() (username string, password string) {
|
||||||
|
return b.username, b.password
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCredentials(username, password string) Credentials {
|
||||||
|
return &basicAuth{
|
||||||
|
username: username,
|
||||||
|
password: password,
|
||||||
|
}
|
||||||
|
}
|
117
options.go
117
options.go
@ -29,10 +29,13 @@ type Limiter interface {
|
|||||||
|
|
||||||
// Options keeps the settings to set up redis connection.
|
// Options keeps the settings to set up redis connection.
|
||||||
type Options struct {
|
type Options struct {
|
||||||
// The network type, either tcp or unix.
|
|
||||||
// Default is tcp.
|
// Network type, either tcp or unix.
|
||||||
|
//
|
||||||
|
// default: is tcp.
|
||||||
Network string
|
Network string
|
||||||
// host:port address.
|
|
||||||
|
// Addr is the address formated as host:port
|
||||||
Addr string
|
Addr string
|
||||||
|
|
||||||
// ClientName will execute the `CLIENT SETNAME ClientName` command for each conn.
|
// ClientName will execute the `CLIENT SETNAME ClientName` command for each conn.
|
||||||
@ -42,21 +45,25 @@ type Options struct {
|
|||||||
// Network and Addr options.
|
// Network and Addr options.
|
||||||
Dialer func(ctx context.Context, network, addr string) (net.Conn, error)
|
Dialer func(ctx context.Context, network, addr string) (net.Conn, error)
|
||||||
|
|
||||||
// Hook that is called when new connection is established.
|
// OnConnect Hook that is called when new connection is established.
|
||||||
OnConnect func(ctx context.Context, cn *Conn) error
|
OnConnect func(ctx context.Context, cn *Conn) error
|
||||||
|
|
||||||
// Protocol 2 or 3. Use the version to negotiate RESP version with redis-server.
|
// Protocol 2 or 3. Use the version to negotiate RESP version with redis-server.
|
||||||
// Default is 3.
|
//
|
||||||
|
// default: 3.
|
||||||
Protocol int
|
Protocol int
|
||||||
// Use the specified Username to authenticate the current connection
|
|
||||||
|
// Username is used to authenticate the current connection
|
||||||
// with one of the connections defined in the ACL list when connecting
|
// with one of the connections defined in the ACL list when connecting
|
||||||
// to a Redis 6.0 instance, or greater, that is using the Redis ACL system.
|
// to a Redis 6.0 instance, or greater, that is using the Redis ACL system.
|
||||||
Username string
|
Username string
|
||||||
// Optional password. Must match the password specified in the
|
|
||||||
// requirepass server configuration option (if connecting to a Redis 5.0 instance, or lower),
|
// Password is an optional password. Must match the password specified in the
|
||||||
|
// `requirepass` server configuration option (if connecting to a Redis 5.0 instance, or lower),
|
||||||
// or the User Password when connecting to a Redis 6.0 instance, or greater,
|
// or the User Password when connecting to a Redis 6.0 instance, or greater,
|
||||||
// that is using the Redis ACL system.
|
// that is using the Redis ACL system.
|
||||||
Password string
|
Password string
|
||||||
|
|
||||||
// CredentialsProvider allows the username and password to be updated
|
// CredentialsProvider allows the username and password to be updated
|
||||||
// before reconnecting. It should return the current username and password.
|
// before reconnecting. It should return the current username and password.
|
||||||
CredentialsProvider func() (username string, password string)
|
CredentialsProvider func() (username string, password string)
|
||||||
@ -67,94 +74,128 @@ type Options struct {
|
|||||||
// There will be a conflict between them; if CredentialsProviderContext exists, we will ignore CredentialsProvider.
|
// There will be a conflict between them; if CredentialsProviderContext exists, we will ignore CredentialsProvider.
|
||||||
CredentialsProviderContext func(ctx context.Context) (username string, password string, err error)
|
CredentialsProviderContext func(ctx context.Context) (username string, password string, err error)
|
||||||
|
|
||||||
// Database to be selected after connecting to the server.
|
// DB is the database to be selected after connecting to the server.
|
||||||
DB int
|
DB int
|
||||||
|
|
||||||
// Maximum number of retries before giving up.
|
// MaxRetries is the maximum number of retries before giving up.
|
||||||
// Default is 3 retries; -1 (not 0) disables retries.
|
// -1 (not 0) disables retries.
|
||||||
|
//
|
||||||
|
// default: 3 retries
|
||||||
MaxRetries int
|
MaxRetries int
|
||||||
// Minimum backoff between each retry.
|
|
||||||
// Default is 8 milliseconds; -1 disables backoff.
|
// MinRetryBackoff is the minimum backoff between each retry.
|
||||||
|
// -1 disables backoff.
|
||||||
|
//
|
||||||
|
// default: 8 milliseconds
|
||||||
MinRetryBackoff time.Duration
|
MinRetryBackoff time.Duration
|
||||||
// Maximum backoff between each retry.
|
|
||||||
// Default is 512 milliseconds; -1 disables backoff.
|
// MaxRetryBackoff is the maximum backoff between each retry.
|
||||||
|
// -1 disables backoff.
|
||||||
|
// default: 512 milliseconds;
|
||||||
MaxRetryBackoff time.Duration
|
MaxRetryBackoff time.Duration
|
||||||
|
|
||||||
// Dial timeout for establishing new connections.
|
// DialTimeout for establishing new connections.
|
||||||
// Default is 5 seconds.
|
//
|
||||||
|
// default: 5 seconds
|
||||||
DialTimeout time.Duration
|
DialTimeout time.Duration
|
||||||
// Timeout for socket reads. If reached, commands will fail
|
|
||||||
|
// ReadTimeout for socket reads. If reached, commands will fail
|
||||||
// with a timeout instead of blocking. Supported values:
|
// with a timeout instead of blocking. Supported values:
|
||||||
// - `0` - default timeout (3 seconds).
|
//
|
||||||
// - `-1` - no timeout (block indefinitely).
|
// - `-1` - no timeout (block indefinitely).
|
||||||
// - `-2` - disables SetReadDeadline calls completely.
|
// - `-2` - disables SetReadDeadline calls completely.
|
||||||
|
//
|
||||||
|
// default: 3 seconds
|
||||||
ReadTimeout time.Duration
|
ReadTimeout time.Duration
|
||||||
// Timeout for socket writes. If reached, commands will fail
|
|
||||||
|
// WriteTimeout for socket writes. If reached, commands will fail
|
||||||
// with a timeout instead of blocking. Supported values:
|
// with a timeout instead of blocking. Supported values:
|
||||||
// - `0` - default timeout (3 seconds).
|
//
|
||||||
// - `-1` - no timeout (block indefinitely).
|
// - `-1` - no timeout (block indefinitely).
|
||||||
// - `-2` - disables SetWriteDeadline calls completely.
|
// - `-2` - disables SetWriteDeadline calls completely.
|
||||||
|
//
|
||||||
|
// default: 3 seconds
|
||||||
WriteTimeout time.Duration
|
WriteTimeout time.Duration
|
||||||
|
|
||||||
// ContextTimeoutEnabled controls whether the client respects context timeouts and deadlines.
|
// ContextTimeoutEnabled controls whether the client respects context timeouts and deadlines.
|
||||||
// See https://redis.uptrace.dev/guide/go-redis-debugging.html#timeouts
|
// See https://redis.uptrace.dev/guide/go-redis-debugging.html#timeouts
|
||||||
ContextTimeoutEnabled bool
|
ContextTimeoutEnabled bool
|
||||||
|
|
||||||
// Type of connection pool.
|
// PoolFIFO type of connection pool.
|
||||||
// true for FIFO pool, false for LIFO pool.
|
//
|
||||||
|
// - true for FIFO pool
|
||||||
|
// - false for LIFO pool.
|
||||||
|
//
|
||||||
// Note that FIFO has slightly higher overhead compared to LIFO,
|
// Note that FIFO has slightly higher overhead compared to LIFO,
|
||||||
// but it helps closing idle connections faster reducing the pool size.
|
// but it helps closing idle connections faster reducing the pool size.
|
||||||
PoolFIFO bool
|
PoolFIFO bool
|
||||||
// Base number of socket connections.
|
|
||||||
|
// PoolSize is the base number of socket connections.
|
||||||
// Default is 10 connections per every available CPU as reported by runtime.GOMAXPROCS.
|
// Default is 10 connections per every available CPU as reported by runtime.GOMAXPROCS.
|
||||||
// If there is not enough connections in the pool, new connections will be allocated in excess of PoolSize,
|
// If there is not enough connections in the pool, new connections will be allocated in excess of PoolSize,
|
||||||
// you can limit it through MaxActiveConns
|
// you can limit it through MaxActiveConns
|
||||||
|
//
|
||||||
|
// default: 10 * runtime.GOMAXPROCS(0)
|
||||||
PoolSize int
|
PoolSize int
|
||||||
// Amount of time client waits for connection if all connections
|
|
||||||
|
// PoolTimeout is the amount of time client waits for connection if all connections
|
||||||
// are busy before returning an error.
|
// are busy before returning an error.
|
||||||
// Default is ReadTimeout + 1 second.
|
//
|
||||||
|
// default: ReadTimeout + 1 second
|
||||||
PoolTimeout time.Duration
|
PoolTimeout time.Duration
|
||||||
// Minimum number of idle connections which is useful when establishing
|
|
||||||
// new connection is slow.
|
// MinIdleConns is the minimum number of idle connections which is useful when establishing
|
||||||
// Default is 0. the idle connections are not closed by default.
|
// new connection is slow. The idle connections are not closed by default.
|
||||||
|
//
|
||||||
|
// default: 0
|
||||||
MinIdleConns int
|
MinIdleConns int
|
||||||
// Maximum number of idle connections.
|
|
||||||
// Default is 0. the idle connections are not closed by default.
|
// MaxIdleConns is the maximum number of idle connections.
|
||||||
|
// The idle connections are not closed by default.
|
||||||
|
//
|
||||||
|
// default: 0
|
||||||
MaxIdleConns int
|
MaxIdleConns int
|
||||||
// Maximum number of connections allocated by the pool at a given time.
|
|
||||||
|
// MaxActiveConns is the maximum number of connections allocated by the pool at a given time.
|
||||||
// When zero, there is no limit on the number of connections in the pool.
|
// When zero, there is no limit on the number of connections in the pool.
|
||||||
|
// If the pool is full, the next call to Get() will block until a connection is released.
|
||||||
MaxActiveConns int
|
MaxActiveConns int
|
||||||
|
|
||||||
// ConnMaxIdleTime is the maximum amount of time a connection may be idle.
|
// ConnMaxIdleTime is the maximum amount of time a connection may be idle.
|
||||||
// Should be less than server's timeout.
|
// Should be less than server's timeout.
|
||||||
//
|
//
|
||||||
// Expired connections may be closed lazily before reuse.
|
// Expired connections may be closed lazily before reuse.
|
||||||
// If d <= 0, connections are not closed due to a connection's idle time.
|
// If d <= 0, connections are not closed due to a connection's idle time.
|
||||||
|
// -1 disables idle timeout check.
|
||||||
//
|
//
|
||||||
// Default is 30 minutes. -1 disables idle timeout check.
|
// default: 30 minutes
|
||||||
ConnMaxIdleTime time.Duration
|
ConnMaxIdleTime time.Duration
|
||||||
|
|
||||||
// ConnMaxLifetime is the maximum amount of time a connection may be reused.
|
// ConnMaxLifetime is the maximum amount of time a connection may be reused.
|
||||||
//
|
//
|
||||||
// Expired connections may be closed lazily before reuse.
|
// Expired connections may be closed lazily before reuse.
|
||||||
// If <= 0, connections are not closed due to a connection's age.
|
// If <= 0, connections are not closed due to a connection's age.
|
||||||
//
|
//
|
||||||
// Default is to not close idle connections.
|
// default: 0
|
||||||
ConnMaxLifetime time.Duration
|
ConnMaxLifetime time.Duration
|
||||||
|
|
||||||
// TLS Config to use. When set, TLS will be negotiated.
|
// TLSConfig to use. When set, TLS will be negotiated.
|
||||||
TLSConfig *tls.Config
|
TLSConfig *tls.Config
|
||||||
|
|
||||||
// Limiter interface used to implement circuit breaker or rate limiter.
|
// Limiter interface used to implement circuit breaker or rate limiter.
|
||||||
Limiter Limiter
|
Limiter Limiter
|
||||||
|
|
||||||
// Enables read only queries on slave/follower nodes.
|
// readOnly enables read only queries on slave/follower nodes.
|
||||||
readOnly bool
|
readOnly bool
|
||||||
|
|
||||||
// Disable set-lib on connect. Default is false.
|
// DisableIndentity set-lib on connect. Default is false.
|
||||||
DisableIndentity bool
|
DisableIndentity bool
|
||||||
|
|
||||||
// Add suffix to client name. Default is empty.
|
// IdentitySuffix - add suffix to client name.
|
||||||
IdentitySuffix string
|
IdentitySuffix string
|
||||||
|
|
||||||
// UnstableResp3 enables Unstable mode for Redis Search module with RESP3.
|
// UnstableResp3 enables Unstable mode for Redis Search module with RESP3.
|
||||||
|
// When unstable mode is enabled, the client will use RESP3 protocol and only be able to use RawResult
|
||||||
UnstableResp3 bool
|
UnstableResp3 bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
50
redis.go
50
redis.go
@ -9,6 +9,7 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/redis/go-redis/v9/auth"
|
||||||
"github.com/redis/go-redis/v9/internal"
|
"github.com/redis/go-redis/v9/internal"
|
||||||
"github.com/redis/go-redis/v9/internal/hscan"
|
"github.com/redis/go-redis/v9/internal/hscan"
|
||||||
"github.com/redis/go-redis/v9/internal/pool"
|
"github.com/redis/go-redis/v9/internal/pool"
|
||||||
@ -282,13 +283,34 @@ func (c *baseClient) _getConn(ctx context.Context) (*pool.Conn, error) {
|
|||||||
return cn, nil
|
return cn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *baseClient) reAuth(ctx context.Context, cn *Conn, credentials auth.Credentials) error {
|
||||||
|
var err error
|
||||||
|
username, password := credentials.BasicAuth()
|
||||||
|
if username != "" {
|
||||||
|
err = cn.AuthACL(ctx, username, password).Err()
|
||||||
|
} else {
|
||||||
|
err = cn.Auth(ctx, password).Err()
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (c *baseClient) initConn(ctx context.Context, cn *pool.Conn) error {
|
func (c *baseClient) initConn(ctx context.Context, cn *pool.Conn) error {
|
||||||
if cn.Inited {
|
if cn.Inited {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
cn.Inited = true
|
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
|
cn.Inited = true
|
||||||
|
connPool := pool.NewSingleConnPool(c.connPool, cn)
|
||||||
|
conn := newConn(c.opt, connPool)
|
||||||
|
|
||||||
|
protocol := c.opt.Protocol
|
||||||
|
// By default, use RESP3 in current version.
|
||||||
|
if protocol < 2 {
|
||||||
|
protocol = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
var authenticated bool
|
||||||
username, password := c.opt.Username, c.opt.Password
|
username, password := c.opt.Username, c.opt.Password
|
||||||
if c.opt.CredentialsProviderContext != nil {
|
if c.opt.CredentialsProviderContext != nil {
|
||||||
if username, password, err = c.opt.CredentialsProviderContext(ctx); err != nil {
|
if username, password, err = c.opt.CredentialsProviderContext(ctx); err != nil {
|
||||||
@ -298,20 +320,10 @@ func (c *baseClient) initConn(ctx context.Context, cn *pool.Conn) error {
|
|||||||
username, password = c.opt.CredentialsProvider()
|
username, password = c.opt.CredentialsProvider()
|
||||||
}
|
}
|
||||||
|
|
||||||
connPool := pool.NewSingleConnPool(c.connPool, cn)
|
|
||||||
conn := newConn(c.opt, connPool)
|
|
||||||
|
|
||||||
var auth bool
|
|
||||||
protocol := c.opt.Protocol
|
|
||||||
// By default, use RESP3 in current version.
|
|
||||||
if protocol < 2 {
|
|
||||||
protocol = 3
|
|
||||||
}
|
|
||||||
|
|
||||||
// for redis-server versions that do not support the HELLO command,
|
// for redis-server versions that do not support the HELLO command,
|
||||||
// RESP2 will continue to be used.
|
// RESP2 will continue to be used.
|
||||||
if err = conn.Hello(ctx, protocol, username, password, "").Err(); err == nil {
|
if err = conn.Hello(ctx, protocol, username, password, "").Err(); err == nil {
|
||||||
auth = true
|
authenticated = true
|
||||||
} else if !isRedisError(err) {
|
} else if !isRedisError(err) {
|
||||||
// When the server responds with the RESP protocol and the result is not a normal
|
// When the server responds with the RESP protocol and the result is not a normal
|
||||||
// execution result of the HELLO command, we consider it to be an indication that
|
// execution result of the HELLO command, we consider it to be an indication that
|
||||||
@ -323,15 +335,13 @@ func (c *baseClient) initConn(ctx context.Context, cn *pool.Conn) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !authenticated && password != "" {
|
||||||
|
err = c.reAuth(ctx, conn, auth.NewCredentials(username, password))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
_, err = conn.Pipelined(ctx, func(pipe Pipeliner) error {
|
_, err = conn.Pipelined(ctx, func(pipe Pipeliner) error {
|
||||||
if !auth && password != "" {
|
|
||||||
if username != "" {
|
|
||||||
pipe.AuthACL(ctx, username, password)
|
|
||||||
} else {
|
|
||||||
pipe.Auth(ctx, password)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.opt.DB > 0 {
|
if c.opt.DB > 0 {
|
||||||
pipe.Select(ctx, c.opt.DB)
|
pipe.Select(ctx, c.opt.DB)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user