mirror of
https://github.com/kataras/iris.git
synced 2026-01-07 20:17:05 +00:00
Version 3.0.0-beta cleaned
This commit is contained in:
176
sessions/providers/redis/redisstore.go
Normal file
176
sessions/providers/redis/redisstore.go
Normal file
@@ -0,0 +1,176 @@
|
||||
package redis
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/sessions/store"
|
||||
"github.com/kataras/iris/utils"
|
||||
)
|
||||
|
||||
/*Notes only for me
|
||||
--------
|
||||
Here we are setting a structure which keeps the current session's values setted by store.Set(key,value)
|
||||
this is the RedisValue struct.
|
||||
if noexists
|
||||
RedisValue := RedisValue{sessionid,values)
|
||||
|
||||
RedisValue.values[thekey]=thevalue
|
||||
|
||||
|
||||
service.Set(store.sid,RedisValue)
|
||||
|
||||
because we are using the same redis service for all sessions, and this is the best way to separate them,
|
||||
without prefix and all that which I tried and failed to deserialize them correctly if the value is string...
|
||||
so again we will keep the current server's sessions into memory
|
||||
and fetch them(the sessions) from the redis at each first session run. Yes this is the fastest way to get/set a session
|
||||
and at the same time they are keep saved to the redis and the GC will cleanup the memory after a while like we are doing
|
||||
with the memory provider. Or just have a values field inside the Store and use just it, yes better simpler approach.
|
||||
Ok then, let's convert it again.
|
||||
*/
|
||||
|
||||
// Values is just a type of a map[interface{}]interface{}
|
||||
type Values map[interface{}]interface{}
|
||||
|
||||
// Store the redis session store
|
||||
type Store struct {
|
||||
sid string
|
||||
lastAccessedTime time.Time
|
||||
values Values
|
||||
cookieLifeDuration time.Duration //used on .Set-> SETEX on redis
|
||||
}
|
||||
|
||||
var _ store.IStore = &Store{}
|
||||
|
||||
// NewStore creates and returns a new store based on the session id(string) and the cookie life duration (time.Duration)
|
||||
func NewStore(sid string, cookieLifeDuration time.Duration) *Store {
|
||||
s := &Store{sid: sid, lastAccessedTime: time.Now(), cookieLifeDuration: cookieLifeDuration}
|
||||
//fetch the values from this session id and copy-> store them
|
||||
val, err := redis.GetBytes(sid)
|
||||
if err == nil {
|
||||
err = utils.DeserializeBytes(val, &s.values)
|
||||
if err != nil {
|
||||
//if deserialization failed
|
||||
s.values = Values{}
|
||||
}
|
||||
|
||||
}
|
||||
if s.values == nil {
|
||||
//if key/sid wasn't found or was found but no entries in it(L72)
|
||||
s.values = Values{}
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// serialize the values to be stored as strings inside the Redis, we panic at any serialization error here
|
||||
func serialize(values Values) []byte {
|
||||
val, err := utils.SerializeBytes(values)
|
||||
if err != nil {
|
||||
panic("On redisstore.serialize: " + err.Error())
|
||||
}
|
||||
|
||||
return val
|
||||
}
|
||||
|
||||
// update updates the real redis store
|
||||
func (s *Store) update() {
|
||||
go redis.Set(s.sid, serialize(s.values), s.cookieLifeDuration.Seconds()) //set/update all the values, in goroutine
|
||||
}
|
||||
|
||||
// GetAll returns all values
|
||||
func (s *Store) GetAll() map[interface{}]interface{} {
|
||||
return s.values
|
||||
}
|
||||
|
||||
// VisitAll loop each one entry and calls the callback function func(key,value)
|
||||
func (s *Store) VisitAll(cb func(k interface{}, v interface{})) {
|
||||
for key := range s.values {
|
||||
cb(key, s.values[key])
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the value of an entry by its key
|
||||
func (s *Store) Get(key interface{}) interface{} {
|
||||
Provider.Update(s.sid)
|
||||
|
||||
if value, found := s.values[key]; found {
|
||||
return value
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetString same as Get but returns as string, if nil then returns an empty string
|
||||
func (s *Store) GetString(key interface{}) string {
|
||||
if value := s.Get(key); value != nil {
|
||||
if v, ok := value.(string); ok {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetInt same as Get but returns as int, if nil then returns -1
|
||||
func (s *Store) GetInt(key interface{}) int {
|
||||
if value := s.Get(key); value != nil {
|
||||
if v, ok := value.(int); ok {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
// Set fills the session with an entry, it receives a key and a value
|
||||
// returns an error, which is always nil
|
||||
func (s *Store) Set(key interface{}, value interface{}) error {
|
||||
s.values[key] = value
|
||||
Provider.Update(s.sid)
|
||||
|
||||
s.update()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete removes an entry by its key
|
||||
// returns an error, which is always nil
|
||||
func (s *Store) Delete(key interface{}) error {
|
||||
delete(s.values, key)
|
||||
Provider.Update(s.sid)
|
||||
s.update()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Clear removes all entries
|
||||
// returns an error, which is always nil
|
||||
func (s *Store) Clear() error {
|
||||
//we are not using the Redis.Delete, I made so work for nothing.. we wanted only the .Set at the end...
|
||||
for key := range s.values {
|
||||
delete(s.values, key)
|
||||
}
|
||||
|
||||
Provider.Update(s.sid)
|
||||
s.update()
|
||||
return nil
|
||||
}
|
||||
|
||||
// ID returns the session id
|
||||
func (s *Store) ID() string {
|
||||
return s.sid
|
||||
}
|
||||
|
||||
// LastAccessedTime returns the last time this session has been used
|
||||
func (s *Store) LastAccessedTime() time.Time {
|
||||
return s.lastAccessedTime
|
||||
}
|
||||
|
||||
// SetLastAccessedTime updates the last accessed time
|
||||
func (s *Store) SetLastAccessedTime(lastacc time.Time) {
|
||||
s.lastAccessedTime = lastacc
|
||||
}
|
||||
|
||||
// Destroy deletes entirely the session, from the memory, the client's cookie and the store
|
||||
func (s *Store) Destroy() {
|
||||
// remove the whole value which is the s.values from real redis
|
||||
redis.Delete(s.sid)
|
||||
}
|
||||
47
sessions/providers/redis/register.go
Normal file
47
sessions/providers/redis/register.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package redis
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/kataras/iris/sessions"
|
||||
"github.com/kataras/iris/sessions/providers/redis/service"
|
||||
"github.com/kataras/iris/sessions/store"
|
||||
)
|
||||
|
||||
func init() {
|
||||
register()
|
||||
}
|
||||
|
||||
var (
|
||||
Provider = sessions.NewProvider("redis")
|
||||
// redis is the default redis service, you can set configs via this object
|
||||
redis = service.New()
|
||||
// Config is just the Redis(service)' config
|
||||
Config = redis.Config
|
||||
|
||||
// Empty() because maybe the user wants to edit the default configs.
|
||||
//the Connect goes to the first NewStore, when user ask for session, so you have the time to change the default configs
|
||||
)
|
||||
|
||||
// register registers itself (the new provider with its memory store) to the sessions providers
|
||||
// must runs only once
|
||||
func register() {
|
||||
// the actual work is here.
|
||||
Provider.NewStore = func(sessionId string, cookieLifeDuration time.Duration) store.IStore {
|
||||
//println("memory.go:49-> requesting new memory store with sessionid: " + sessionId)
|
||||
if !redis.Connected {
|
||||
redis.Connect()
|
||||
_, err := redis.PingPong()
|
||||
if err != nil {
|
||||
if err != nil {
|
||||
// don't use to get the logger, just prin these to the console... atm
|
||||
println("Redis Connection error on iris/sessions/providers/redisstore.Connect: " + err.Error())
|
||||
println("But don't panic, auto-switching to memory store right now!")
|
||||
}
|
||||
}
|
||||
}
|
||||
return NewStore(sessionId, cookieLifeDuration)
|
||||
}
|
||||
|
||||
sessions.Register(Provider)
|
||||
}
|
||||
279
sessions/providers/redis/service/service.go
Normal file
279
sessions/providers/redis/service/service.go
Normal file
@@ -0,0 +1,279 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/garyburd/redigo/redis"
|
||||
"github.com/kataras/iris/config"
|
||||
"github.com/kataras/iris/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrRedisClosed an error with message 'Redis is already closed'
|
||||
ErrRedisClosed = errors.New("Redis is already closed")
|
||||
// ErrKeyNotFound an error with message 'Key $thekey doesn't found'
|
||||
ErrKeyNotFound = errors.New("Key '%s' doesn't found")
|
||||
)
|
||||
|
||||
// Service the Redis service, contains the config and the redis pool
|
||||
type Service struct {
|
||||
// Connected is true when the Service has already connected
|
||||
Connected bool
|
||||
// Config the redis config for this redis
|
||||
Config *config.Redis
|
||||
pool *redis.Pool
|
||||
}
|
||||
|
||||
// PingPong sends a ping and receives a pong, if no pong received then returns false and filled error
|
||||
func (r *Service) PingPong() (bool, error) {
|
||||
c := r.pool.Get()
|
||||
defer c.Close()
|
||||
msg, err := c.Do("PING")
|
||||
if err != nil || msg == nil {
|
||||
return false, err
|
||||
}
|
||||
return (msg == "PONG"), nil
|
||||
}
|
||||
|
||||
// CloseConnection closes the redis connection
|
||||
func (r *Service) CloseConnection() error {
|
||||
if r.pool != nil {
|
||||
return r.pool.Close()
|
||||
}
|
||||
return ErrRedisClosed.Return()
|
||||
}
|
||||
|
||||
// Set sets to the redis
|
||||
// key string, value string, you can use utils.Serialize(&myobject{}) to convert an object to []byte
|
||||
func (r *Service) Set(key string, value []byte, maxageseconds ...float64) (err error) { // map[interface{}]interface{}) (err error) {
|
||||
maxage := config.DefaultRedisMaxAgeSeconds //1 year
|
||||
c := r.pool.Get()
|
||||
defer c.Close()
|
||||
if err = c.Err(); err != nil {
|
||||
return
|
||||
}
|
||||
if len(maxageseconds) > 0 {
|
||||
if max := maxageseconds[0]; max >= 0 {
|
||||
maxage = max
|
||||
}
|
||||
}
|
||||
_, err = c.Do("SETEX", r.Config.Prefix+key, maxage, value)
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns value, err by its key
|
||||
// you can use utils.Deserialize((.Get("yourkey"),&theobject{})
|
||||
//returns nil and a filled error if something wrong happens
|
||||
func (r *Service) Get(key string) (interface{}, error) {
|
||||
c := r.pool.Get()
|
||||
defer c.Close()
|
||||
if err := c.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
redisVal, err := c.Do("GET", r.Config.Prefix+key)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if redisVal == nil {
|
||||
return nil, ErrKeyNotFound.Format(key)
|
||||
}
|
||||
return redisVal, nil
|
||||
}
|
||||
|
||||
// GetBytes returns value, err by its key
|
||||
// you can use utils.Deserialize((.GetBytes("yourkey"),&theobject{})
|
||||
//returns nil and a filled error if something wrong happens
|
||||
func (r *Service) GetBytes(key string) ([]byte, error) {
|
||||
c := r.pool.Get()
|
||||
defer c.Close()
|
||||
if err := c.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
redisVal, err := c.Do("GET", r.Config.Prefix+key)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if redisVal == nil {
|
||||
return nil, ErrKeyNotFound.Format(key)
|
||||
}
|
||||
|
||||
return redis.Bytes(redisVal, err)
|
||||
}
|
||||
|
||||
// GetString returns value, err by its key
|
||||
// you can use utils.Deserialize((.GetString("yourkey"),&theobject{})
|
||||
//returns empty string and a filled error if something wrong happens
|
||||
func (r *Service) GetString(key string) (string, error) {
|
||||
redisVal, err := r.Get(key)
|
||||
if redisVal == nil {
|
||||
return "", ErrKeyNotFound.Format(key)
|
||||
}
|
||||
|
||||
sVal, err := redis.String(redisVal, err)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return sVal, nil
|
||||
}
|
||||
|
||||
// GetInt returns value, err by its key
|
||||
// you can use utils.Deserialize((.GetInt("yourkey"),&theobject{})
|
||||
//returns -1 int and a filled error if something wrong happens
|
||||
func (r *Service) GetInt(key string) (int, error) {
|
||||
redisVal, err := r.Get(key)
|
||||
if redisVal == nil {
|
||||
return -1, ErrKeyNotFound.Format(key)
|
||||
}
|
||||
|
||||
intVal, err := redis.Int(redisVal, err)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return intVal, nil
|
||||
}
|
||||
|
||||
// GetStringMap returns map[string]string, err by its key
|
||||
//returns nil and a filled error if something wrong happens
|
||||
func (r *Service) GetStringMap(key string) (map[string]string, error) {
|
||||
redisVal, err := r.Get(key)
|
||||
if redisVal == nil {
|
||||
return nil, ErrKeyNotFound.Format(key)
|
||||
}
|
||||
|
||||
_map, err := redis.StringMap(redisVal, err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return _map, nil
|
||||
}
|
||||
|
||||
// GetAll returns all keys and their values from a specific key (map[string]string)
|
||||
// returns a filled error if something bad happened
|
||||
func (r *Service) GetAll(key string) (map[string]string, error) {
|
||||
c := r.pool.Get()
|
||||
defer c.Close()
|
||||
if err := c.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reply, err := c.Do("HGETALL", r.Config.Prefix+key)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if reply == nil {
|
||||
return nil, ErrKeyNotFound.Format(key)
|
||||
}
|
||||
|
||||
return redis.StringMap(reply, err)
|
||||
|
||||
}
|
||||
|
||||
// GetAllKeysByPrefix returns all []string keys by a key prefix from the redis
|
||||
func (r *Service) GetAllKeysByPrefix(prefix string) ([]string, error) {
|
||||
c := r.pool.Get()
|
||||
defer c.Close()
|
||||
if err := c.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reply, err := c.Do("KEYS", r.Config.Prefix+prefix)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if reply == nil {
|
||||
return nil, ErrKeyNotFound.Format(prefix)
|
||||
}
|
||||
return redis.Strings(reply, err)
|
||||
|
||||
}
|
||||
|
||||
// Delete removes redis entry by specific key
|
||||
func (r *Service) Delete(key string) error {
|
||||
c := r.pool.Get()
|
||||
defer c.Close()
|
||||
if _, err := c.Do("DEL", r.Config.Prefix+key); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func dial(network string, addr string, pass string) (redis.Conn, error) {
|
||||
if network == "" {
|
||||
network = config.DefaultRedisNetwork
|
||||
}
|
||||
if addr == "" {
|
||||
addr = config.DefaultRedisAddr
|
||||
}
|
||||
c, err := redis.Dial(network, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if pass != "" {
|
||||
if _, err := c.Do("AUTH", pass); err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return c, err
|
||||
}
|
||||
|
||||
// Connect connects to the redis, called only once
|
||||
func (r *Service) Connect() {
|
||||
c := r.Config
|
||||
|
||||
if c.IdleTimeout <= 0 {
|
||||
c.IdleTimeout = config.DefaultRedisIdleTimeout
|
||||
}
|
||||
|
||||
if c.Network == "" {
|
||||
c.Network = config.DefaultRedisNetwork
|
||||
}
|
||||
|
||||
if c.Addr == "" {
|
||||
c.Addr = config.DefaultRedisAddr
|
||||
}
|
||||
|
||||
if c.MaxAgeSeconds <= 0 {
|
||||
c.MaxAgeSeconds = config.DefaultRedisMaxAgeSeconds
|
||||
}
|
||||
|
||||
pool := &redis.Pool{IdleTimeout: config.DefaultRedisIdleTimeout, MaxIdle: c.MaxIdle, MaxActive: c.MaxActive}
|
||||
pool.TestOnBorrow = func(c redis.Conn, t time.Time) error {
|
||||
_, err := c.Do("PING")
|
||||
return err
|
||||
}
|
||||
|
||||
if c.Database != "" {
|
||||
pool.Dial = func() (redis.Conn, error) {
|
||||
red, err := dial(c.Network, c.Addr, c.Password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := red.Do("SELECT", c.Database); err != nil {
|
||||
red.Close()
|
||||
return nil, err
|
||||
}
|
||||
return red, err
|
||||
}
|
||||
} else {
|
||||
pool.Dial = func() (redis.Conn, error) {
|
||||
return dial(c.Network, c.Addr, c.Password)
|
||||
}
|
||||
}
|
||||
r.Connected = true
|
||||
r.pool = pool
|
||||
}
|
||||
|
||||
// New returns a Redis service filled by the passed config
|
||||
// to connect call the .Connect()
|
||||
func New(cfg ...config.Redis) *Service {
|
||||
c := config.DefaultRedis().Merge(cfg)
|
||||
r := &Service{pool: &redis.Pool{}, Config: &c}
|
||||
return r
|
||||
}
|
||||
Reference in New Issue
Block a user