database.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. package sessions
  2. import (
  3. "errors"
  4. "reflect"
  5. "sync"
  6. "time"
  7. "github.com/kataras/iris/v12/context"
  8. "github.com/kataras/iris/v12/core/memstore"
  9. "github.com/kataras/golog"
  10. )
  11. // ErrNotImplemented is returned when a particular feature is not yet implemented yet.
  12. // It can be matched directly, i.e: `isNotImplementedError := sessions.ErrNotImplemented.Equal(err)`.
  13. var ErrNotImplemented = errors.New("not implemented yet")
  14. // Database is the interface which all session databases should implement
  15. // By design it doesn't support any type of cookie session like other frameworks.
  16. // I want to protect you, believe me.
  17. // The scope of the database is to store somewhere the sessions in order to
  18. // keep them after restarting the server, nothing more.
  19. //
  20. // Synchronization are made automatically, you can register one using `UseDatabase`.
  21. //
  22. // Look the `sessiondb` folder for databases implementations.
  23. type Database interface {
  24. // SetLogger should inject a logger to this Database.
  25. SetLogger(*golog.Logger)
  26. // Acquire receives a session's lifetime from the database,
  27. // if the return value is LifeTime{} then the session manager sets the life time based on the expiration duration lives in configuration.
  28. Acquire(sid string, expires time.Duration) memstore.LifeTime
  29. // OnUpdateExpiration should re-set the expiration (ttl) of the session entry inside the database,
  30. // it is fired on `ShiftExpiration` and `UpdateExpiration`.
  31. // If the database does not support change of ttl then the session entry will be cloned to another one
  32. // and the old one will be removed, it depends on the chosen database storage.
  33. //
  34. // Check of error is required, if error returned then the rest session's keys are not proceed.
  35. //
  36. // If a database does not support this feature then an `ErrNotImplemented` will be returned instead.
  37. OnUpdateExpiration(sid string, newExpires time.Duration) error
  38. // Set sets a key value of a specific session.
  39. // The "immutable" input argument depends on the store, it may not implement it at all.
  40. Set(sid string, key string, value interface{}, ttl time.Duration, immutable bool) error
  41. // Get retrieves a session value based on the key.
  42. Get(sid string, key string) interface{}
  43. // Decode binds the "outPtr" to the value associated to the provided "key".
  44. Decode(sid, key string, outPtr interface{}) error
  45. // Visit loops through all session keys and values.
  46. Visit(sid string, cb func(key string, value interface{})) error
  47. // Len returns the length of the session's entries (keys).
  48. Len(sid string) int
  49. // Delete removes a session key value based on its key.
  50. Delete(sid string, key string) (deleted bool)
  51. // Clear removes all session key values but it keeps the session entry.
  52. Clear(sid string) error
  53. // Release destroys the session, it clears and removes the session entry,
  54. // session manager will create a new session ID on the next request after this call.
  55. Release(sid string) error
  56. // Close should terminate the database connection. It's called automatically on interrupt signals.
  57. Close() error
  58. }
  59. // DatabaseRequestHandler is an optional interface that a sessions database
  60. // can implement. It contains a single EndRequest method which is fired
  61. // on the very end of the request life cycle. It should be used to Flush
  62. // any local session's values to the client.
  63. type DatabaseRequestHandler interface {
  64. EndRequest(ctx *context.Context, session *Session)
  65. }
  66. type mem struct {
  67. values map[string]*memstore.Store
  68. mu sync.RWMutex
  69. }
  70. var _ Database = (*mem)(nil)
  71. func newMemDB() Database { return &mem{values: make(map[string]*memstore.Store)} }
  72. func (s *mem) SetLogger(*golog.Logger) {}
  73. func (s *mem) Acquire(sid string, expires time.Duration) memstore.LifeTime {
  74. s.mu.Lock()
  75. s.values[sid] = new(memstore.Store)
  76. s.mu.Unlock()
  77. return memstore.LifeTime{}
  78. }
  79. // Do nothing, the `LifeTime` of the Session will be managed by the callers automatically on memory-based storage.
  80. func (s *mem) OnUpdateExpiration(string, time.Duration) error { return nil }
  81. // immutable depends on the store, it may not implement it at all.
  82. func (s *mem) Set(sid string, key string, value interface{}, _ time.Duration, immutable bool) error {
  83. s.mu.RLock()
  84. store, ok := s.values[sid]
  85. s.mu.RUnlock()
  86. if ok {
  87. store.Save(key, value, immutable)
  88. }
  89. return nil
  90. }
  91. func (s *mem) Get(sid string, key string) interface{} {
  92. s.mu.RLock()
  93. store, ok := s.values[sid]
  94. s.mu.RUnlock()
  95. if ok {
  96. return store.Get(key)
  97. }
  98. return nil
  99. }
  100. func (s *mem) Decode(sid string, key string, outPtr interface{}) error {
  101. v := s.Get(sid, key)
  102. if v != nil {
  103. reflect.ValueOf(outPtr).Set(reflect.ValueOf(v))
  104. }
  105. return nil
  106. }
  107. func (s *mem) Visit(sid string, cb func(key string, value interface{})) error {
  108. s.mu.RLock()
  109. store, ok := s.values[sid]
  110. s.mu.RUnlock()
  111. if ok {
  112. store.Visit(cb)
  113. }
  114. return nil
  115. }
  116. func (s *mem) Len(sid string) int {
  117. s.mu.RLock()
  118. store, ok := s.values[sid]
  119. s.mu.RUnlock()
  120. if ok {
  121. return store.Len()
  122. }
  123. return 0
  124. }
  125. func (s *mem) Delete(sid string, key string) (deleted bool) {
  126. s.mu.RLock()
  127. store, ok := s.values[sid]
  128. s.mu.RUnlock()
  129. if ok {
  130. deleted = store.Remove(key)
  131. }
  132. return
  133. }
  134. func (s *mem) Clear(sid string) error {
  135. s.mu.RLock()
  136. store, ok := s.values[sid]
  137. s.mu.RUnlock()
  138. if ok {
  139. store.Reset()
  140. }
  141. return nil
  142. }
  143. func (s *mem) Release(sid string) error {
  144. s.mu.Lock()
  145. delete(s.values, sid)
  146. s.mu.Unlock()
  147. return nil
  148. }
  149. func (s *mem) Close() error { return nil }