106 lines
2.1 KiB
Go

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
}