1
0
mirror of https://github.com/alexedwards/scs.git synced 2025-07-13 01:00:17 +02:00
Files
scs/memstore/memstore.go
2021-08-22 18:08:19 +02:00

142 lines
3.4 KiB
Go

package memstore
import (
"sync"
"time"
)
type item struct {
object []byte
expiration int64
}
// MemStore represents the session store.
type MemStore struct {
items map[string]item
mu sync.RWMutex
stopCleanup chan bool
}
// New returns a new MemStore instance, with a background cleanup goroutine that
// runs every minute to remove expired session data.
func New() *MemStore {
return NewWithCleanupInterval(time.Minute)
}
// NewWithCleanupInterval returns a new MemStore instance. The cleanupInterval
// parameter controls how frequently expired session data is removed by the
// background cleanup goroutine. Setting it to 0 prevents the cleanup goroutine
// from running (i.e. expired sessions will not be removed).
func NewWithCleanupInterval(cleanupInterval time.Duration) *MemStore {
m := &MemStore{
items: make(map[string]item),
}
if cleanupInterval > 0 {
go m.startCleanup(cleanupInterval)
}
return m
}
// Find returns the data for a given session token from the MemStore instance.
// If the session token is not found or is expired, the returned exists flag will
// be set to false.
func (m *MemStore) Find(token string) ([]byte, bool, error) {
m.mu.RLock()
defer m.mu.RUnlock()
item, found := m.items[token]
if !found {
return nil, false, nil
}
if time.Now().UnixNano() > item.expiration {
return nil, false, nil
}
return item.object, true, nil
}
// Commit adds a session token and data to the MemStore instance with the given
// expiry time. If the session token already exists, then the data and expiry
// time are updated.
func (m *MemStore) Commit(token string, b []byte, expiry time.Time) error {
m.mu.Lock()
m.items[token] = item{
object: b,
expiration: expiry.UnixNano(),
}
m.mu.Unlock()
return nil
}
// Delete removes a session token and corresponding data from the MemStore
// instance.
func (m *MemStore) Delete(token string) error {
m.mu.Lock()
delete(m.items, token)
m.mu.Unlock()
return nil
}
// All returns a map containing the token and data for all active (i.e.
// not expired) sessions.
func (m *MemStore) All() (map[string][]byte, error) {
m.mu.RLock()
defer m.mu.RUnlock()
var mm = make(map[string][]byte)
for token, item := range m.items {
if item.expiration > time.Now().UnixNano() {
mm[token] = item.object
}
}
return mm, nil
}
func (m *MemStore) startCleanup(interval time.Duration) {
m.stopCleanup = make(chan bool)
ticker := time.NewTicker(interval)
for {
select {
case <-ticker.C:
m.deleteExpired()
case <-m.stopCleanup:
ticker.Stop()
return
}
}
}
// StopCleanup terminates the background cleanup goroutine for the MemStore
// instance. It's rare to terminate this; generally MemStore instances and
// their cleanup goroutines are intended to be long-lived and run for the lifetime
// of your application.
//
// There may be occasions though when your use of the MemStore is transient.
// An example is creating a new MemStore instance in a test function. In this
// scenario, the cleanup goroutine (which will run forever) will prevent the
// MemStore object from being garbage collected even after the test function
// has finished. You can prevent this by manually calling StopCleanup.
func (m *MemStore) StopCleanup() {
if m.stopCleanup != nil {
m.stopCleanup <- true
}
}
func (m *MemStore) deleteExpired() {
now := time.Now().UnixNano()
m.mu.Lock()
for token, item := range m.items {
if now > item.expiration {
delete(m.items, token)
}
}
m.mu.Unlock()
}