// Package memory implements sessions saved into memory is gone when stop the server package memory import ( "container/list" "sync" "time" "git.mtux.eu/darkgopher/session" ) var pder = &ProviderMemory{list: list.New()} func init() { pder.sessions = make(map[string]*list.Element, 0) session.Register("memory", pder) } // SessionMemory implement sessionma.Session interface only in memory type SessionMemory struct { sid string atime time.Time data map[any]any } func (sm *SessionMemory) resolvepanic(err *error) { if r := recover(); r != nil { *err = r.(error) } } // Set - func (sm *SessionMemory) Set(k any, v any) (err error) { defer pder.updateAtime(sm.sid) defer sm.resolvepanic(&err) sm.data[k] = v return } // Get - func (sm *SessionMemory) Get(k any) (v any, err error) { defer pder.updateAtime(sm.sid) defer sm.resolvepanic(&err) return sm.data[k], err } // Delete - func (sm *SessionMemory) Delete(k any) (err error) { defer pder.updateAtime(sm.sid) delete(sm.data, k) return } // SessionID - func (sm *SessionMemory) SessionID() string { return sm.sid } // ProviderMemory implement memory session provider type ProviderMemory struct { lock sync.Mutex sessions map[string]*list.Element //save to RAM list *list.List //for GC } func (pder *ProviderMemory) updateAtime(sid string) { pder.lock.Lock() defer pder.lock.Unlock() if ssel, ok := pder.sessions[sid]; ok { ssel.Value.(*SessionMemory).atime = time.Now() pder.list.MoveToFront(ssel) } } // Init create new session store for sid func (pder *ProviderMemory) Init(sid string) (ses session.Session, err error) { pder.lock.Lock() defer pder.lock.Unlock() data := make(map[any]any, 0) sess := &SessionMemory{sid, time.Now(), data} sessel := pder.list.PushBack(sess) pder.sessions[sid] = sessel return sess, nil } // Exists return true if session with sid exists func (pder *ProviderMemory) Exists(sid string) (ex bool) { if _, ex := pder.sessions[sid]; ex { return ex } return } // read return existing unexpired session or create new func (pder *ProviderMemory) Read(sid string) (ses session.Session, err error) { pder.lock.Lock() defer pder.lock.Unlock() if ssel, ok := pder.sessions[sid]; ok { return ssel.Value.(*SessionMemory), nil } return pder.Init(sid) } // Destroy delete session storage with sid func (pder *ProviderMemory) Destroy(sid string) (err error) { pder.lock.Lock() defer pder.lock.Unlock() if ssel, ok := pder.sessions[sid]; ok { delete(pder.sessions, sid) pder.list.Remove(ssel) return nil } return } // RegenerateID replace session ID to new one and preserve all data func (pder *ProviderMemory) RegenerateID(oldsid, newsid string) (err error) { pder.lock.Lock() defer pder.lock.Unlock() if ssel, ok := pder.sessions[oldsid]; ok { ssel.Value.(*SessionMemory).sid = newsid delete(pder.sessions, oldsid) pder.sessions[newsid] = ssel } return } // GC iterate all sessions and delete expired func (pder *ProviderMemory) GC(maxlifetime int64) { pder.lock.Lock() defer pder.lock.Unlock() for { ssel := pder.list.Back() if ssel == nil { break } if ssel.Value.(*SessionMemory).atime.UnixMilli()+(maxlifetime*session.MilisPerSec()) < time.Now().UnixMilli() { pder.list.Remove(ssel) delete(pder.sessions, ssel.Value.(*SessionMemory).sid) } else { break } } }