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.

  1. package memory
  2. import (
  3. "container/list"
  4. "github.com/astaxie/session"
  5. "sync"
  6. "time"
  7. )
  8. var pder = &Provider{list: list.New()}
  9. type SessionStore struct {
  10. sid string // unique session id
  11. timeAccessed time.Time // last access time
  12. value map[interface{}]interface{} // session value stored inside
  13. }
  14. func (st *SessionStore) Set(key, value interface{}) error {
  15. st.value[key] = value
  16. pder.SessionUpdate(st.sid)
  17. return nil
  18. }
  19. func (st *SessionStore) Get(key interface{}) interface{} {
  20. pder.SessionUpdate(st.sid)
  21. if v, ok := st.value[key]; ok {
  22. return v
  23. } else {
  24. return nil
  25. }
  26. return nil
  27. }
  28. func (st *SessionStore) Delete(key interface{}) error {
  29. delete(st.value, key)
  30. pder.SessionUpdate(st.sid)
  31. return nil
  32. }
  33. func (st *SessionStore) SessionID() string {
  34. return st.sid
  35. }
  36. type Provider struct {
  37. lock sync.Mutex // lock
  38. sessions map[string]*list.Element // save in memory
  39. list *list.List // gc
  40. }
  41. func (pder *Provider) SessionInit(sid string) (session.Session, error) {
  42. pder.lock.Lock()
  43. defer pder.lock.Unlock()
  44. v := make(map[interface{}]interface{}, 0)
  45. newsess := &SessionStore{sid: sid, timeAccessed: time.Now(), value: v}
  46. element := pder.list.PushBack(newsess)
  47. pder.sessions[sid] = element
  48. return newsess, nil
  49. }
  50. func (pder *Provider) SessionRead(sid string) (session.Session, error) {
  51. if element, ok := pder.sessions[sid]; ok {
  52. return element.Value.(*SessionStore), nil
  53. } else {
  54. sess, err := pder.SessionInit(sid)
  55. return sess, err
  56. }
  57. return nil, nil
  58. }
  59. func (pder *Provider) SessionDestroy(sid string) error {
  60. if element, ok := pder.sessions[sid]; ok {
  61. delete(pder.sessions, sid)
  62. pder.list.Remove(element)
  63. return nil
  64. }
  65. return nil
  66. }
  67. func (pder *Provider) SessionGC(maxlifetime int64) {
  68. pder.lock.Lock()
  69. defer pder.lock.Unlock()
  70. for {
  71. element := pder.list.Back()
  72. if element == nil {
  73. break
  74. }
  75. if (element.Value.(*SessionStore).timeAccessed.Unix() + maxlifetime) < time.Now().Unix() {
  76. pder.list.Remove(element)
  77. delete(pder.sessions, element.Value.(*SessionStore).sid)
  78. } else {
  79. break
  80. }
  81. }
  82. }
  83. func (pder *Provider) SessionUpdate(sid string) error {
  84. pder.lock.Lock()
  85. defer pder.lock.Unlock()
  86. if element, ok := pder.sessions[sid]; ok {
  87. element.Value.(*SessionStore).timeAccessed = time.Now()
  88. pder.list.MoveToFront(element)
  89. return nil
  90. }
  91. return nil
  92. }
  93. func init() {
  94. pder.sessions = make(map[string]*list.Element, 0)
  95. session.Register("memory", pder)
  96. }

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?

  1. import (
  2. "github.com/astaxie/session"
  3. _ "github.com/astaxie/session/providers/memory"
  4. )

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:

  1. var globalSessions *session.Manager
  2. // initialize in init() function
  3. func init() {
  4. globalSessions, _ = session.NewManager("memory", "gosessionid", 3600)
  5. go globalSessions.GC()
  6. }