-
-
Notifications
You must be signed in to change notification settings - Fork 2.5k
/
database.go
176 lines (149 loc) · 5.31 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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
package sessions
import (
"errors"
"reflect"
"sync"
"time"
"github.com/kataras/iris/v12/context"
"github.com/kataras/iris/v12/core/memstore"
"github.com/kataras/golog"
)
// ErrNotImplemented is returned when a particular feature is not yet implemented yet.
// It can be matched directly, i.e: `isNotImplementedError := sessions.ErrNotImplemented.Equal(err)`.
var ErrNotImplemented = errors.New("not implemented yet")
// Database is the interface which all session databases should implement
// By design it doesn't support any type of cookie session like other frameworks.
// I want to protect you, believe me.
// The scope of the database is to store somewhere the sessions in order to
// keep them after restarting the server, nothing more.
//
// Synchronization are made automatically, you can register one using `UseDatabase`.
//
// Look the `sessiondb` folder for databases implementations.
type Database interface {
// SetLogger should inject a logger to this Database.
SetLogger(*golog.Logger)
// 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.
Acquire(sid string, expires time.Duration) memstore.LifeTime
// OnUpdateExpiration should re-set the expiration (ttl) of the session entry inside the database,
// it is fired on `ShiftExpiration` and `UpdateExpiration`.
// If the database does not support change of ttl then the session entry will be cloned to another one
// and the old one will be removed, it depends on the chosen database storage.
//
// Check of error is required, if error returned then the rest session's keys are not proceed.
//
// If a database does not support this feature then an `ErrNotImplemented` will be returned instead.
OnUpdateExpiration(sid string, newExpires time.Duration) error
// Set sets a key value of a specific session.
// The "immutable" input argument depends on the store, it may not implement it at all.
Set(sid string, key string, value interface{}, ttl time.Duration, immutable bool) error
// Get retrieves a session value based on the key.
Get(sid string, key string) interface{}
// Decode binds the "outPtr" to the value associated to the provided "key".
Decode(sid, key string, outPtr interface{}) error
// Visit loops through all session keys and values.
Visit(sid string, cb func(key string, value interface{})) error
// Len returns the length of the session's entries (keys).
Len(sid string) int
// Delete removes a session key value based on its key.
Delete(sid string, key string) (deleted bool)
// Clear removes all session key values but it keeps the session entry.
Clear(sid string) error
// 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.
Release(sid string) error
// Close should terminate the database connection. It's called automatically on interrupt signals.
Close() error
}
// DatabaseRequestHandler is an optional interface that a sessions database
// can implement. It contains a single EndRequest method which is fired
// on the very end of the request life cycle. It should be used to Flush
// any local session's values to the client.
type DatabaseRequestHandler interface {
EndRequest(ctx *context.Context, session *Session)
}
type mem struct {
values map[string]*memstore.Store
mu sync.RWMutex
}
var _ Database = (*mem)(nil)
func newMemDB() Database { return &mem{values: make(map[string]*memstore.Store)} }
func (s *mem) SetLogger(*golog.Logger) {}
func (s *mem) Acquire(sid string, expires time.Duration) memstore.LifeTime {
s.mu.Lock()
s.values[sid] = new(memstore.Store)
s.mu.Unlock()
return memstore.LifeTime{}
}
// Do nothing, the `LifeTime` of the Session will be managed by the callers automatically on memory-based storage.
func (s *mem) OnUpdateExpiration(string, time.Duration) error { return nil }
// immutable depends on the store, it may not implement it at all.
func (s *mem) Set(sid string, key string, value interface{}, _ time.Duration, immutable bool) error {
s.mu.RLock()
store, ok := s.values[sid]
s.mu.RUnlock()
if ok {
store.Save(key, value, immutable)
}
return nil
}
func (s *mem) Get(sid string, key string) interface{} {
s.mu.RLock()
store, ok := s.values[sid]
s.mu.RUnlock()
if ok {
return store.Get(key)
}
return nil
}
func (s *mem) Decode(sid string, key string, outPtr interface{}) error {
v := s.Get(sid, key)
if v != nil {
reflect.ValueOf(outPtr).Set(reflect.ValueOf(v))
}
return nil
}
func (s *mem) Visit(sid string, cb func(key string, value interface{})) error {
s.mu.RLock()
store, ok := s.values[sid]
s.mu.RUnlock()
if ok {
store.Visit(cb)
}
return nil
}
func (s *mem) Len(sid string) int {
s.mu.RLock()
store, ok := s.values[sid]
s.mu.RUnlock()
if ok {
return store.Len()
}
return 0
}
func (s *mem) Delete(sid string, key string) (deleted bool) {
s.mu.RLock()
store, ok := s.values[sid]
s.mu.RUnlock()
if ok {
deleted = store.Remove(key)
}
return
}
func (s *mem) Clear(sid string) error {
s.mu.RLock()
store, ok := s.values[sid]
s.mu.RUnlock()
if ok {
store.Reset()
}
return nil
}
func (s *mem) Release(sid string) error {
s.mu.Lock()
delete(s.values, sid)
s.mu.Unlock()
return nil
}
func (s *mem) Close() error { return nil }