kuvia2/vendor/github.com/flamego/session/redis/redis.go
2022-01-15 00:09:03 +01:00

135 lines
3.5 KiB
Go

// Copyright 2021 Flamego. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package redis
import (
"context"
"fmt"
"time"
"github.com/go-redis/redis/v8"
"github.com/pkg/errors"
"github.com/flamego/session"
)
var _ session.Store = (*redisStore)(nil)
// redisStore is a Redis implementation of the session store.
type redisStore struct {
client *redis.Client // The client connection
lifetime time.Duration // The duration to have access to a session before being recycled
encoder session.Encoder // The encoder to encode the session data before saving
decoder session.Decoder // The decoder to decode binary to session data after reading
}
// newRedisStore returns a new Redis session store based on given configuration.
func newRedisStore(cfg Config) *redisStore {
return &redisStore{
client: cfg.client,
lifetime: cfg.Lifetime,
encoder: cfg.Encoder,
decoder: cfg.Decoder,
}
}
func (s *redisStore) Exist(ctx context.Context, sid string) bool {
result, err := s.client.Exists(ctx, sid).Result()
return err == nil && result == 1
}
func (s *redisStore) Read(ctx context.Context, sid string) (session.Session, error) {
binary, err := s.client.Get(ctx, sid).Result()
if err != nil {
if err == redis.Nil {
return session.NewBaseSession(sid, s.encoder), nil
}
return nil, errors.Wrap(err, "get")
}
data, err := s.decoder([]byte(binary))
if err != nil {
return nil, errors.Wrap(err, "decode")
}
sess := session.NewBaseSession(sid, s.encoder)
sess.SetData(data)
return sess, nil
}
func (s *redisStore) Destroy(ctx context.Context, sid string) error {
return s.client.Del(ctx, sid).Err()
}
func (s *redisStore) Save(ctx context.Context, sess session.Session) error {
binary, err := sess.Encode()
if err != nil {
return errors.Wrap(err, "encode")
}
err = s.client.SetEX(ctx, sess.ID(), binary, s.lifetime).Err()
if err != nil {
return errors.Wrap(err, "set")
}
return nil
}
func (s *redisStore) GC(_ context.Context) error {
return nil
}
// Options keeps the settings to set up Redis client connection.
type Options = redis.Options
// Config contains options for the Redis session store.
type Config struct {
// For tests only
client *redis.Client
// Options is the settings to set up Redis client connection.
Options *Options
// Lifetime is the duration to have no access to a session before being
// recycled. Default is 3600 seconds.
Lifetime time.Duration
// Encoder is the encoder to encode session data. Default is session.GobEncoder.
Encoder session.Encoder
// Decoder is the decoder to decode session data. Default is session.GobDecoder.
Decoder session.Decoder
}
// Initer returns the session.Initer for the Redis session store.
func Initer() session.Initer {
return func(ctx context.Context, args ...interface{}) (session.Store, error) {
var cfg *Config
for i := range args {
switch v := args[i].(type) {
case Config:
cfg = &v
}
}
if cfg == nil {
return nil, fmt.Errorf("config object with the type '%T' not found", Config{})
} else if cfg.Options == nil && cfg.client == nil {
return nil, errors.New("empty Options")
}
if cfg.client == nil {
cfg.client = redis.NewClient(cfg.Options)
}
if cfg.Lifetime.Seconds() < 1 {
cfg.Lifetime = 3600 * time.Second
}
if cfg.Encoder == nil {
cfg.Encoder = session.GobEncoder
}
if cfg.Decoder == nil {
cfg.Decoder = session.GobDecoder
}
return newRedisStore(*cfg), nil
}
}