1
0
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:
Makis Maropoulos
2016-05-30 17:08:09 +03:00
commit c26668a489
114 changed files with 14552 additions and 0 deletions

View 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)
}

View 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)
}

View 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
}