- new module cooler: Install, Handle, OpenRedis (no URL in errors), RateLimitStore (atomic fixed-window Lua script with MemoryStore semantics, per-instance fallback) - serve installs cooler and publishes the Redis surf.Store before BuildRouter; BuildRouter looks up a published Store, route:list keeps the memory store - config keys http.rate_limit.store and redis.url; go-redis v9.23.0 - cooler README, root table row, surf README and rate-limiting, configuration and scaling docs
47 lines
1.6 KiB
Go
47 lines
1.6 KiB
Go
package cooler
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
|
|
"github.com/redis/go-redis/v9"
|
|
"github.com/redis/go-redis/v9/maintnotifications"
|
|
)
|
|
|
|
// errInvalidRedisURL is the only error OpenRedis returns for a URL that does
|
|
// not parse. It never wraps or quotes the parser's error: url.Parse echoes the
|
|
// whole URL, password included.
|
|
var errInvalidRedisURL = errors.New("cooler: " + RedisURLKey + " is not a valid redis://, rediss:// or unix:// URL")
|
|
|
|
// OpenRedis builds a go-redis client from a redis:// (plain), rediss:// (TLS)
|
|
// or unix:// URL, such as the value of redis.url. It does not dial: go-redis
|
|
// connects on the first command. A URL that does not parse yields an error that
|
|
// names redis.url and never contains the URL or its credentials.
|
|
//
|
|
// The client skips the CLIENT SETINFO and maintenance-notification handshakes,
|
|
// which older servers and test doubles do not know.
|
|
func OpenRedis(ctx context.Context, rawURL string) (*redis.Client, error) {
|
|
_ = ctx // reserved: go-redis does not dial at construction
|
|
rawURL = strings.TrimSpace(rawURL)
|
|
if rawURL == "" {
|
|
return nil, errors.New("cooler: " + RedisURLKey + " is empty")
|
|
}
|
|
opt, err := redis.ParseURL(rawURL)
|
|
if err != nil {
|
|
return nil, errInvalidRedisURL
|
|
}
|
|
opt.DisableIdentity = true
|
|
opt.MaintNotificationsConfig = &maintnotifications.Config{Mode: maintnotifications.ModeDisabled}
|
|
return redis.NewClient(opt), nil
|
|
}
|
|
|
|
// redisEndpoint is the part of a client's address that may be logged: the
|
|
// host and port (or socket path), never the URL, user or password.
|
|
func redisEndpoint(c *redis.Client) string {
|
|
if c == nil {
|
|
return ""
|
|
}
|
|
return c.Options().Addr
|
|
}
|