-
Notifications
You must be signed in to change notification settings - Fork 3
/
session_inmem.go
106 lines (92 loc) · 2.2 KB
/
session_inmem.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
package cypress
import (
"sync"
"time"
"go.uber.org/zap"
)
type sessionItem struct {
session *Session
expiration time.Time
}
type inMemorySessionStore struct {
sessions map[string]*sessionItem
lock *sync.RWMutex
gcTicker *time.Ticker
exitChan chan bool
}
// NewInMemorySessionStore creates an in memory session store
func NewInMemorySessionStore() SessionStore {
store := &inMemorySessionStore{
sessions: make(map[string]*sessionItem),
lock: new(sync.RWMutex),
gcTicker: time.NewTicker(5 * time.Minute),
exitChan: make(chan bool),
}
go func() {
for {
select {
case <-store.gcTicker.C:
store.doGC()
break
case <-store.exitChan:
return
}
}
}()
return store
}
// Close closes the session store
func (store *inMemorySessionStore) Close() {
store.exitChan <- true
store.gcTicker.Stop()
close(store.exitChan)
}
// Save saves the session into store
func (store *inMemorySessionStore) Save(session *Session, timeout time.Duration) error {
store.lock.Lock()
defer store.lock.Unlock()
if !session.IsValid {
delete(store.sessions, session.ID)
} else {
item, ok := store.sessions[session.ID]
if ok && item.session.IsValid && item.expiration.After(time.Now()) {
item.expiration = time.Now().Add(timeout)
} else {
store.sessions[session.ID] = &sessionItem{
session: session,
expiration: time.Now().Add(timeout),
}
}
}
return nil
}
// Get retrieves the session by session ID
func (store *inMemorySessionStore) Get(id string) (*Session, error) {
store.lock.RLock()
defer store.lock.RUnlock()
item, ok := store.sessions[id]
if !ok || item.expiration.Before(time.Now()) {
return nil, ErrSessionNotFound
}
item.session.isDirty = false
return item.session, nil
}
func (store *inMemorySessionStore) doGC() {
keysToRemove := make([]string, 0)
func() {
store.lock.RLock()
defer store.lock.RUnlock()
now := time.Now()
for key, value := range store.sessions {
if value.expiration.Before(now) {
keysToRemove = append(keysToRemove, key)
}
}
}()
store.lock.Lock()
defer store.lock.Unlock()
for _, key := range keysToRemove {
delete(store.sessions, key)
zap.L().Debug("session released by GC", zap.String("session", key))
}
}