mirror of
https://github.com/rls-moe/nyx
synced 2024-11-16 22:12:24 +00:00
146 lines
3.6 KiB
Go
146 lines
3.6 KiB
Go
|
/*
|
||
|
|
||
|
An in-memory session store implementation.
|
||
|
|
||
|
*/
|
||
|
|
||
|
package session
|
||
|
|
||
|
import (
|
||
|
"log"
|
||
|
"sync"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
// In-memory session Store implementation.
|
||
|
type inMemStore struct {
|
||
|
sessions map[string]Session // Map of sessions (mapped from ID)
|
||
|
mux *sync.RWMutex // mutex to synchronize access to sessions
|
||
|
ticker *time.Ticker // Ticker for the session cleaner
|
||
|
closeTicker chan struct{} // Channel to signal close for the session cleaner
|
||
|
}
|
||
|
|
||
|
// InMemStoreOptions defines options that may be passed when creating a new in-memory Store.
|
||
|
// All fields are optional; default value will be used for any field that has the zero value.
|
||
|
type InMemStoreOptions struct {
|
||
|
// Session cleaner check interval, default is 10 seconds.
|
||
|
SessCleanerInterval time.Duration
|
||
|
}
|
||
|
|
||
|
// Pointer to zero value of InMemStoreOptions to be reused for efficiency.
|
||
|
var zeroInMemStoreOptions = new(InMemStoreOptions)
|
||
|
|
||
|
// NewInMemStore returns a new, in-memory session Store with the default options.
|
||
|
// Default values of options are listed in the InMemStoreOptions type.
|
||
|
// The returned Store has an automatic session cleaner which runs
|
||
|
// in its own goroutine.
|
||
|
func NewInMemStore() Store {
|
||
|
return NewInMemStoreOptions(zeroInMemStoreOptions)
|
||
|
}
|
||
|
|
||
|
// NewInMemStoreOptions returns a new, in-memory session Store with the specified options.
|
||
|
// The returned Store has an automatic session cleaner which runs
|
||
|
// in its own goroutine.
|
||
|
func NewInMemStoreOptions(o *InMemStoreOptions) Store {
|
||
|
s := &inMemStore{
|
||
|
sessions: make(map[string]Session),
|
||
|
mux: &sync.RWMutex{},
|
||
|
closeTicker: make(chan struct{}),
|
||
|
}
|
||
|
|
||
|
interval := o.SessCleanerInterval
|
||
|
if interval == 0 {
|
||
|
interval = 10 * time.Second
|
||
|
}
|
||
|
|
||
|
go s.sessCleaner(interval)
|
||
|
|
||
|
return s
|
||
|
}
|
||
|
|
||
|
// sessCleaner periodically checks whether sessions have timed out
|
||
|
// in an endless loop. If a session has timed out, removes it.
|
||
|
// This method is to be started as a new goroutine.
|
||
|
func (s *inMemStore) sessCleaner(interval time.Duration) {
|
||
|
ticker := time.NewTicker(interval)
|
||
|
|
||
|
for {
|
||
|
select {
|
||
|
case <-s.closeTicker:
|
||
|
// We are being shut down...
|
||
|
ticker.Stop()
|
||
|
return
|
||
|
case now := <-ticker.C:
|
||
|
// Do a sweep.
|
||
|
// Remove is very rare compared to the number of checks, so:
|
||
|
// "Quick" check with read-lock to see if there's anything to remove:
|
||
|
// Note: Session.Access() is called with s.mux, the same mutex we use
|
||
|
// when looking for timed-out sessions, so we're good.
|
||
|
needRemove := func() bool {
|
||
|
s.mux.RLock() // Read lock is enough
|
||
|
defer s.mux.RUnlock()
|
||
|
|
||
|
for _, sess := range s.sessions {
|
||
|
if now.Sub(sess.Accessed()) > sess.Timeout() {
|
||
|
return true
|
||
|
}
|
||
|
}
|
||
|
return false
|
||
|
}()
|
||
|
if !needRemove {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
// Remove required:
|
||
|
func() {
|
||
|
s.mux.Lock() // Read-write lock required
|
||
|
defer s.mux.Unlock()
|
||
|
|
||
|
for _, sess := range s.sessions {
|
||
|
if now.Sub(sess.Accessed()) > sess.Timeout() {
|
||
|
log.Println("Session timed out:", sess.ID())
|
||
|
delete(s.sessions, sess.ID())
|
||
|
}
|
||
|
}
|
||
|
}()
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Get is to implement Store.Get().
|
||
|
func (s *inMemStore) Get(id string) Session {
|
||
|
s.mux.RLock()
|
||
|
defer s.mux.RUnlock()
|
||
|
|
||
|
sess := s.sessions[id]
|
||
|
if sess == nil {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
sess.Access()
|
||
|
return sess
|
||
|
}
|
||
|
|
||
|
// Add is to implement Store.Add().
|
||
|
func (s *inMemStore) Add(sess Session) {
|
||
|
s.mux.Lock()
|
||
|
defer s.mux.Unlock()
|
||
|
|
||
|
log.Println("Session added:", sess.ID())
|
||
|
s.sessions[sess.ID()] = sess
|
||
|
}
|
||
|
|
||
|
// Remove is to implement Store.Remove().
|
||
|
func (s *inMemStore) Remove(sess Session) {
|
||
|
s.mux.Lock()
|
||
|
defer s.mux.Unlock()
|
||
|
|
||
|
log.Println("Session removed:", sess.ID())
|
||
|
delete(s.sessions, sess.ID())
|
||
|
}
|
||
|
|
||
|
// Close is to implement Store.Close().
|
||
|
func (s *inMemStore) Close() {
|
||
|
close(s.closeTicker)
|
||
|
}
|