nyx/vendor/github.com/icza/session/session.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()
}