package session import ( "crypto/rand" "encoding/base64" "fmt" "io" "net/http" "sync" "time" ) var ( providers = make(map[string]IProvider) session *Manager lock sync.Mutex ) type Manager struct { provider IProvider cookieName string maxLifetime int64 lock sync.Mutex } type IManager interface { SessionStart(w http.ResponseWriter, r *http.Request) ISession GC() } type IProvider interface { SessionInit(sid string) (ISession, error) SessionRead(sid string) (ISession, error) SessionDestroy(sid string) error SessionGC(maxLifeTime int64) } type ISession interface { Set(key, value interface{}) error Get(key interface{}) interface{} Delete(key interface{}) error SessionID() string } func NewManager(providerName, cookieName string, maxLifetime int64) (*Manager, error) { provider, ok := providers[providerName] if !ok { return nil, fmt.Errorf("session: unknown provider %q", providerName) } if session == nil { lock.Lock() defer lock.Unlock() session = &Manager{provider: provider, cookieName: cookieName, maxLifetime: maxLifetime} } return session, nil } func (m *Manager) SessionStart(w http.ResponseWriter, r *http.Request) ISession { m.lock.Lock() defer m.lock.Unlock() cookie, err := r.Cookie(m.cookieName) var session ISession if err != nil || cookie.Value == "" { sid := m.sessionId() session, _ = m.provider.SessionInit(sid) cookie := http.Cookie{Name: m.cookieName, Value: sid, Path: "/", HttpOnly: true, MaxAge: int(m.maxLifetime)} http.SetCookie(w, &cookie) } else { sid := cookie.Value session, _ = m.provider.SessionRead(sid) } return session } func (m *Manager) GC() { m.lock.Lock() defer m.lock.Unlock() m.provider.SessionGC(m.maxLifetime) time.AfterFunc(time.Duration(m.maxLifetime), func() { m.GC() }) } func (m *Manager) sessionId() string { b := make([]byte, 32) if _, err := io.ReadFull(rand.Reader, b); err != nil { return "" } return base64.URLEncoding.EncodeToString(b) } func Register(name string, provider IProvider) { if providers[name] != nil { return } if provider == nil { panic("session: Register provider is nil") } providers[name] = provider }