# 6.3 Session storage We introduced a simple session manager's working principles in the previous section, and among other things, we defined a session storage interface. In this section, I'm going to show you an example of a memory based session storage engine that implements this interface. You can tailor this to other forms of session storage as well. ```Go package memory import ( "container/list" "github.com/astaxie/session" "sync" "time" ) var pder = &Provider{list: list.New()} type SessionStore struct { sid string // unique session id timeAccessed time.Time // last access time value map[interface{}]interface{} // session value stored inside } func (st *SessionStore) Set(key, value interface{}) error { st.value[key] = value pder.SessionUpdate(st.sid) return nil } func (st *SessionStore) Get(key interface{}) interface{} { pder.SessionUpdate(st.sid) if v, ok := st.value[key]; ok { return v } else { return nil } return nil } func (st *SessionStore) Delete(key interface{}) error { delete(st.value, key) pder.SessionUpdate(st.sid) return nil } func (st *SessionStore) SessionID() string { return st.sid } type Provider struct { lock sync.Mutex // lock sessions map[string]*list.Element // save in memory list *list.List // gc } func (pder *Provider) SessionInit(sid string) (session.Session, error) { pder.lock.Lock() defer pder.lock.Unlock() v := make(map[interface{}]interface{}, 0) newsess := &SessionStore{sid: sid, timeAccessed: time.Now(), value: v} element := pder.list.PushBack(newsess) pder.sessions[sid] = element return newsess, nil } func (pder *Provider) SessionRead(sid string) (session.Session, error) { if element, ok := pder.sessions[sid]; ok { return element.Value.(*SessionStore), nil } else { sess, err := pder.SessionInit(sid) return sess, err } return nil, nil } func (pder *Provider) SessionDestroy(sid string) error { if element, ok := pder.sessions[sid]; ok { delete(pder.sessions, sid) pder.list.Remove(element) return nil } return nil } func (pder *Provider) SessionGC(maxlifetime int64) { pder.lock.Lock() defer pder.lock.Unlock() for { element := pder.list.Back() if element == nil { break } if (element.Value.(*SessionStore).timeAccessed.Unix() + maxlifetime) < time.Now().Unix() { pder.list.Remove(element) delete(pder.sessions, element.Value.(*SessionStore).sid) } else { break } } } func (pder *Provider) SessionUpdate(sid string) error { pder.lock.Lock() defer pder.lock.Unlock() if element, ok := pder.sessions[sid]; ok { element.Value.(*SessionStore).timeAccessed = time.Now() pder.list.MoveToFront(element) return nil } return nil } func init() { pder.sessions = make(map[string]*list.Element, 0) session.Register("memory", pder) } ``` The above example implements a memory based session storage mechanism. It uses its `init()` function to register this storage engine to the session manager. So how do we register this engine from our main program? ```Go import ( "github.com/astaxie/session" _ "github.com/astaxie/session/providers/memory" ) ``` We use the blank import mechanism (which will invoke the package's `init()` function automatically) to register this engine to a session manager. We then use the following code to initialize the session manager: ```Go var globalSessions *session.Manager // initialize in init() function func init() { globalSessions, _ = session.NewManager("memory", "gosessionid", 3600) go globalSessions.GC() } ``` ## Links - [Directory](preface.md) - Previous section: [How to use sessions in Go](06.2.md) - Next section: [Prevent session hijacking](06.4.md)