mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 18:51:03 +01:00
190 lines
4.9 KiB
Go
190 lines
4.9 KiB
Go
package redis
|
|
|
|
import (
|
|
"sync"
|
|
"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[string]interface{}
|
|
type Values map[string]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
|
|
mu sync.Mutex
|
|
}
|
|
|
|
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[string]interface{} {
|
|
return s.values
|
|
}
|
|
|
|
// VisitAll loop each one entry and calls the callback function func(key,value)
|
|
func (s *Store) VisitAll(cb func(k string, 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 string) interface{} {
|
|
Provider.Update(s.sid)
|
|
if value, found := s.values[key]; found {
|
|
s.mu.Unlock()
|
|
return value
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetString same as Get but returns as string, if nil then returns an empty string
|
|
func (s *Store) GetString(key string) 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 string) 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 string, value interface{}) error {
|
|
s.mu.Lock()
|
|
s.values[key] = value
|
|
s.mu.Unlock()
|
|
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 string) error {
|
|
s.mu.Lock()
|
|
delete(s.values, key)
|
|
s.mu.Unlock()
|
|
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...
|
|
s.mu.Lock()
|
|
for key := range s.values {
|
|
delete(s.values, key)
|
|
}
|
|
s.mu.Unlock()
|
|
|
|
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)
|
|
s.mu.Lock()
|
|
for key := range s.values {
|
|
delete(s.values, key)
|
|
}
|
|
s.mu.Unlock()
|
|
|
|
}
|