forked from kataras/iris
-
Notifications
You must be signed in to change notification settings - Fork 0
/
database.go
162 lines (134 loc) · 4.41 KB
/
database.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package redis
import (
"runtime"
"time"
"github.com/kataras/golog"
"github.com/kataras/iris/sessions"
"github.com/kataras/iris/sessions/sessiondb/redis/service"
)
// Database the redis back-end session database for the sessions.
type Database struct {
redis *service.Service
}
var _ sessions.Database = (*Database)(nil)
// New returns a new redis database.
func New(cfg ...service.Config) *Database {
db := &Database{redis: service.New(cfg...)}
db.redis.Connect()
_, err := db.redis.PingPong()
if err != nil {
golog.Debugf("error connecting to redis: %v", err)
return nil
}
runtime.SetFinalizer(db, closeDB)
return db
}
// Config returns the configuration for the redis server bridge, you can change them.
func (db *Database) Config() *service.Config {
return db.redis.Config
}
// Acquire receives a session's lifetime from the database,
// if the return value is LifeTime{} then the session manager sets the life time based on the expiration duration lives in configuration.
func (db *Database) Acquire(sid string, expires time.Duration) sessions.LifeTime {
seconds, hasExpiration, found := db.redis.TTL(sid)
if !found {
// not found, create an entry with ttl and return an empty lifetime, session manager will do its job.
if err := db.redis.Set(sid, sid, int64(expires.Seconds())); err != nil {
golog.Debug(err)
}
return sessions.LifeTime{} // session manager will handle the rest.
}
if !hasExpiration {
return sessions.LifeTime{}
}
return sessions.LifeTime{Time: time.Now().Add(time.Duration(seconds) * time.Second)}
}
// OnUpdateExpiration will re-set the database's session's entry ttl.
// https://redis.io/commands/expire#refreshing-expires
func (db *Database) OnUpdateExpiration(sid string, newExpires time.Duration) error {
return db.redis.UpdateTTLMany(sid, int64(newExpires.Seconds()))
}
const delim = "_"
func makeKey(sid, key string) string {
return sid + delim + key
}
// Set sets a key value of a specific session.
// Ignore the "immutable".
func (db *Database) Set(sid string, lifetime sessions.LifeTime, key string, value interface{}, immutable bool) {
valueBytes, err := sessions.DefaultTranscoder.Marshal(value)
if err != nil {
golog.Error(err)
return
}
if err = db.redis.Set(makeKey(sid, key), valueBytes, int64(lifetime.DurationUntilExpiration().Seconds())); err != nil {
golog.Debug(err)
}
}
// Get retrieves a session value based on the key.
func (db *Database) Get(sid string, key string) (value interface{}) {
db.get(makeKey(sid, key), &value)
return
}
func (db *Database) get(key string, outPtr interface{}) {
data, err := db.redis.Get(key)
if err != nil {
// not found.
return
}
if err = sessions.DefaultTranscoder.Unmarshal(data.([]byte), outPtr); err != nil {
golog.Debugf("unable to unmarshal value of key: '%s': %v", key, err)
}
}
func (db *Database) keys(sid string) []string {
keys, err := db.redis.GetKeys(sid + delim)
if err != nil {
golog.Debugf("unable to get all redis keys of session '%s': %v", sid, err)
return nil
}
return keys
}
// Visit loops through all session keys and values.
func (db *Database) Visit(sid string, cb func(key string, value interface{})) {
keys := db.keys(sid)
for _, key := range keys {
var value interface{} // new value each time, we don't know what user will do in "cb".
db.get(key, &value)
cb(key, value)
}
}
// Len returns the length of the session's entries (keys).
func (db *Database) Len(sid string) (n int) {
return len(db.keys(sid))
}
// Delete removes a session key value based on its key.
func (db *Database) Delete(sid string, key string) (deleted bool) {
err := db.redis.Delete(makeKey(sid, key))
if err != nil {
golog.Error(err)
}
return err == nil
}
// Clear removes all session key values but it keeps the session entry.
func (db *Database) Clear(sid string) {
keys := db.keys(sid)
for _, key := range keys {
if err := db.redis.Delete(key); err != nil {
golog.Debugf("unable to delete session '%s' value of key: '%s': %v", sid, key, err)
}
}
}
// Release destroys the session, it clears and removes the session entry,
// session manager will create a new session ID on the next request after this call.
func (db *Database) Release(sid string) {
// clear all $sid-$key.
db.Clear(sid)
// and remove the $sid.
db.redis.Delete(sid)
}
// Close terminates the redis connection.
func (db *Database) Close() error {
return closeDB(db)
}
func closeDB(db *Database) error {
return db.redis.CloseConnection()
}