mirror of
https://github.com/rls-moe/nyx
synced 2024-11-14 22:12:24 +00:00
232 lines
6.4 KiB
Go
232 lines
6.4 KiB
Go
|
/*
|
||
|
|
||
|
Session interface and its implementation.
|
||
|
|
||
|
*/
|
||
|
|
||
|
package session
|
||
|
|
||
|
import (
|
||
|
"crypto/rand"
|
||
|
"encoding/base64"
|
||
|
"io"
|
||
|
"sync"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
// Session is the (HTTP) session interface.
|
||
|
// We can use it to store and retrieve constant and variable attributes from it.
|
||
|
type Session interface {
|
||
|
// ID returns the id of the session.
|
||
|
ID() string
|
||
|
|
||
|
// New tells if the session is new.
|
||
|
// Implementation is based on whether created and access times are equal.
|
||
|
New() bool
|
||
|
|
||
|
// CAttr returns the value of an attribute provided at session creation.
|
||
|
// These attributes cannot be changes during the lifetime of a session,
|
||
|
// so they can be accessed safely without synchronization. Exampe is storing the
|
||
|
// authenticated user.
|
||
|
CAttr(name string) interface{}
|
||
|
|
||
|
// Attr returns the value of an attribute stored in the session.
|
||
|
// Safe for concurrent use.
|
||
|
Attr(name string) interface{}
|
||
|
|
||
|
// SetAttr sets the value of an attribute stored in the session.
|
||
|
// Pass the nil value to delete the attribute.
|
||
|
// Safe for concurrent use.
|
||
|
SetAttr(name string, value interface{})
|
||
|
|
||
|
// Attrs returns a copy of all the attribute values stored in the session.
|
||
|
// Safe for concurrent use.
|
||
|
Attrs() map[string]interface{}
|
||
|
|
||
|
// Created returns the session creation time.
|
||
|
Created() time.Time
|
||
|
|
||
|
// Accessed returns the time when the session was last accessed.
|
||
|
Accessed() time.Time
|
||
|
|
||
|
// Timeout returns the session timeout.
|
||
|
// A session may be removed automatically if it is not accessed for this duration.
|
||
|
Timeout() time.Duration
|
||
|
|
||
|
// Mutex returns the RW mutex of the session.
|
||
|
// It is used to synchronize access/modification of the state stored in the session.
|
||
|
// It can be used if session-level synchronization is required.
|
||
|
// Important! If Session values are marshalled / unmarshalled
|
||
|
// (e.g. multi server instance environment such as Google AppEngine),
|
||
|
// this mutex may be different for each Session value and thus
|
||
|
// it can only be used to session-value level synchronization!
|
||
|
Mutex() *sync.RWMutex
|
||
|
|
||
|
// Access registers an access to the session,
|
||
|
// updates its last accessed time to the current time.
|
||
|
// Users do not need to call this as the session store is responsible for that.
|
||
|
Access()
|
||
|
}
|
||
|
|
||
|
// Session implementation.
|
||
|
// Fields are exported so a session may be marshalled / unmarshalled.
|
||
|
type sessionImpl struct {
|
||
|
IDF string // ID of the session
|
||
|
CreatedF time.Time // Creation time
|
||
|
AccessedF time.Time // Last accessed time
|
||
|
CAttrsF map[string]interface{} // Constant attributes specified at session creation
|
||
|
AttrsF map[string]interface{} // Attributes stored in the session
|
||
|
TimeoutF time.Duration // Session timeout
|
||
|
mux *sync.RWMutex // RW mutex to synchronize session state access
|
||
|
}
|
||
|
|
||
|
// SessOptions defines options that may be passed when creating a new Session.
|
||
|
// All fields are optional; default value will be used for any field that has the zero value.
|
||
|
type SessOptions struct {
|
||
|
// Constant attributes of the session. These be will available via the Session.CAttr() method, without synchronization.
|
||
|
// Values from the map will be copied, and will be available via Session.CAttr().
|
||
|
CAttrs map[string]interface{}
|
||
|
|
||
|
// Initial, non-constant attributes to be stored in the session.
|
||
|
// Values from the map will be copied, and will be available via Session.Attr() and Session.Attrs,
|
||
|
// and may be changed with Session.SetAttr().
|
||
|
Attrs map[string]interface{}
|
||
|
|
||
|
// Session timeout, default is 30 minutes.
|
||
|
Timeout time.Duration
|
||
|
|
||
|
// Byte-length of the information that builds up the session ids.
|
||
|
// Using Base-64 encoding, id length will be this multiplied by 4/3 chars.
|
||
|
// Default value is 18 (which means length of ID will be 24 chars).
|
||
|
IDLength int
|
||
|
}
|
||
|
|
||
|
// Pointer to zero value of SessOptions to be reused for efficiency.
|
||
|
var zeroSessOptions = new(SessOptions)
|
||
|
|
||
|
// NewSession creates a new Session with the default options.
|
||
|
// Default values of options are listed in the SessOptions type.
|
||
|
func NewSession() Session {
|
||
|
return NewSessionOptions(zeroSessOptions)
|
||
|
}
|
||
|
|
||
|
// NewSessionOptions creates a new Session with the specified options.
|
||
|
func NewSessionOptions(o *SessOptions) Session {
|
||
|
now := time.Now()
|
||
|
idLength := o.IDLength
|
||
|
if idLength <= 0 {
|
||
|
idLength = 18
|
||
|
}
|
||
|
timeout := o.Timeout
|
||
|
if timeout == 0 {
|
||
|
timeout = 30 * time.Minute
|
||
|
}
|
||
|
|
||
|
sess := sessionImpl{
|
||
|
IDF: genID(idLength),
|
||
|
CreatedF: now,
|
||
|
AccessedF: now,
|
||
|
AttrsF: make(map[string]interface{}),
|
||
|
TimeoutF: timeout,
|
||
|
mux: &sync.RWMutex{},
|
||
|
}
|
||
|
|
||
|
if len(o.CAttrs) > 0 {
|
||
|
sess.CAttrsF = make(map[string]interface{}, len(o.CAttrs))
|
||
|
for k, v := range o.CAttrs {
|
||
|
sess.CAttrsF[k] = v
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for k, v := range o.Attrs {
|
||
|
sess.AttrsF[k] = v
|
||
|
}
|
||
|
|
||
|
return &sess
|
||
|
}
|
||
|
|
||
|
// genID generates a secure, random session id using the crypto/rand package.
|
||
|
func genID(length int) string {
|
||
|
r := make([]byte, length)
|
||
|
io.ReadFull(rand.Reader, r)
|
||
|
return base64.URLEncoding.EncodeToString(r)
|
||
|
}
|
||
|
|
||
|
// ID is to implement Session.ID().
|
||
|
func (s *sessionImpl) ID() string {
|
||
|
return s.IDF
|
||
|
}
|
||
|
|
||
|
// New is to implement Session.New().
|
||
|
func (s *sessionImpl) New() bool {
|
||
|
return s.CreatedF == s.AccessedF
|
||
|
}
|
||
|
|
||
|
// CAttr is to implement Session.CAttr().
|
||
|
func (s *sessionImpl) CAttr(name string) interface{} {
|
||
|
return s.CAttrsF[name]
|
||
|
}
|
||
|
|
||
|
// Attr is to implement Session.Attr().
|
||
|
func (s *sessionImpl) Attr(name string) interface{} {
|
||
|
s.mux.RLock()
|
||
|
defer s.mux.RUnlock()
|
||
|
|
||
|
return s.AttrsF[name]
|
||
|
}
|
||
|
|
||
|
// SetAttr is to implement Session.SetAttr().
|
||
|
func (s *sessionImpl) SetAttr(name string, value interface{}) {
|
||
|
s.mux.Lock()
|
||
|
defer s.mux.Unlock()
|
||
|
|
||
|
if value == nil {
|
||
|
delete(s.AttrsF, name)
|
||
|
} else {
|
||
|
s.AttrsF[name] = value
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Attrs is to implement Session.Attrs().
|
||
|
func (s *sessionImpl) Attrs() map[string]interface{} {
|
||
|
s.mux.RLock()
|
||
|
defer s.mux.RUnlock()
|
||
|
|
||
|
m := make(map[string]interface{}, len(s.AttrsF))
|
||
|
for k, v := range s.AttrsF {
|
||
|
m[k] = v
|
||
|
}
|
||
|
return m
|
||
|
}
|
||
|
|
||
|
// Created is to implement Session.Created().
|
||
|
func (s *sessionImpl) Created() time.Time {
|
||
|
return s.CreatedF
|
||
|
}
|
||
|
|
||
|
// Accessed is to implement Session.Accessed().
|
||
|
func (s *sessionImpl) Accessed() time.Time {
|
||
|
s.mux.RLock()
|
||
|
defer s.mux.RUnlock()
|
||
|
|
||
|
return s.AccessedF
|
||
|
}
|
||
|
|
||
|
// Timeout is to implement Session.Timeout().
|
||
|
func (s *sessionImpl) Timeout() time.Duration {
|
||
|
return s.TimeoutF
|
||
|
}
|
||
|
|
||
|
// Mutex is to implement Session.Mutex().
|
||
|
func (s *sessionImpl) Mutex() *sync.RWMutex {
|
||
|
return s.mux
|
||
|
}
|
||
|
|
||
|
// Access is to implement Session.Access().
|
||
|
func (s *sessionImpl) Access() {
|
||
|
s.mux.Lock()
|
||
|
defer s.mux.Unlock()
|
||
|
|
||
|
s.AccessedF = time.Now()
|
||
|
}
|