provider.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. package sessions
  2. import (
  3. "errors"
  4. "sync"
  5. "time"
  6. "github.com/kataras/iris/v12/context"
  7. )
  8. type (
  9. // provider contains the sessions and external databases (load and update).
  10. // It's the session memory manager
  11. provider struct {
  12. mu sync.RWMutex
  13. sessions map[string]*Session
  14. db Database
  15. dbRequestHandler DatabaseRequestHandler
  16. destroyListeners []DestroyListener
  17. }
  18. )
  19. // newProvider returns a new sessions provider
  20. func newProvider() *provider {
  21. p := &provider{
  22. sessions: make(map[string]*Session),
  23. db: newMemDB(),
  24. }
  25. return p
  26. }
  27. // RegisterDatabase sets a session database.
  28. func (p *provider) RegisterDatabase(db Database) {
  29. if db == nil {
  30. return
  31. }
  32. p.mu.Lock() // for any case
  33. p.db = db
  34. if dbreq, ok := db.(DatabaseRequestHandler); ok {
  35. p.dbRequestHandler = dbreq
  36. }
  37. p.mu.Unlock()
  38. }
  39. // newSession returns a new session from sessionid
  40. func (p *provider) newSession(man *Sessions, sid string, expires time.Duration) *Session {
  41. sess := &Session{
  42. sid: sid,
  43. Man: man,
  44. provider: p,
  45. }
  46. onExpire := func() {
  47. p.mu.Lock()
  48. p.deleteSession(sess)
  49. p.mu.Unlock()
  50. }
  51. lifetime := p.db.Acquire(sid, expires)
  52. // simple and straight:
  53. if !lifetime.IsZero() {
  54. // if stored time is not zero
  55. // start a timer based on the stored time, if not expired.
  56. lifetime.Revive(onExpire)
  57. } else {
  58. // Remember: if db not exist or it has been expired
  59. // then the stored time will be zero(see loadSessionFromDB) and the values will be empty.
  60. //
  61. // Even if the database has an unlimited session (possible by a previous app run)
  62. // priority to the "expires" is given,
  63. // again if <=0 then it does nothing.
  64. lifetime.Begin(expires, onExpire)
  65. }
  66. sess.Lifetime = &lifetime
  67. return sess
  68. }
  69. // Init creates the session and returns it
  70. func (p *provider) Init(man *Sessions, sid string, expires time.Duration) *Session {
  71. newSession := p.newSession(man, sid, expires)
  72. newSession.isNew = true
  73. p.mu.Lock()
  74. p.sessions[sid] = newSession
  75. p.mu.Unlock()
  76. return newSession
  77. }
  78. func (p *provider) EndRequest(ctx *context.Context, session *Session) {
  79. if p.dbRequestHandler != nil {
  80. p.dbRequestHandler.EndRequest(ctx, session)
  81. }
  82. }
  83. // ErrNotFound may be returned from `UpdateExpiration` of a non-existing or
  84. // invalid session entry from memory storage or databases.
  85. // Usage:
  86. //
  87. // if err != nil && err.Is(err, sessions.ErrNotFound) {
  88. // [handle error...]
  89. // }
  90. var ErrNotFound = errors.New("session not found")
  91. // UpdateExpiration resets the expiration of a session.
  92. // if expires > 0 then it will try to update the expiration and destroy task is delayed.
  93. // if expires <= 0 then it does nothing it returns nil, to destroy a session call the `Destroy` func instead.
  94. //
  95. // If the session is not found, it returns a `NotFound` error, this can only happen when you restart the server and you used the memory-based storage(default),
  96. // because the call of the provider's `UpdateExpiration` is always called when the client has a valid session cookie.
  97. //
  98. // If a backend database is used then it may return an `ErrNotImplemented` error if the underline database does not support this operation.
  99. func (p *provider) UpdateExpiration(sid string, expires time.Duration) error {
  100. if expires <= 0 {
  101. return nil
  102. }
  103. p.mu.RLock()
  104. sess, found := p.sessions[sid]
  105. p.mu.RUnlock()
  106. if !found {
  107. return ErrNotFound
  108. }
  109. sess.Lifetime.Shift(expires)
  110. return p.db.OnUpdateExpiration(sid, expires)
  111. }
  112. // Read returns the store which sid parameter belongs
  113. func (p *provider) Read(man *Sessions, sid string, expires time.Duration) *Session {
  114. p.mu.RLock()
  115. sess, found := p.sessions[sid]
  116. p.mu.RUnlock()
  117. if found {
  118. sess.mu.Lock()
  119. sess.isNew = false
  120. sess.mu.Unlock()
  121. sess.runFlashGC() // run the flash messages GC, new request here of existing session
  122. return sess
  123. }
  124. return p.Init(man, sid, expires) // if not found create new
  125. }
  126. func (p *provider) registerDestroyListener(ln DestroyListener) {
  127. if ln == nil {
  128. return
  129. }
  130. p.destroyListeners = append(p.destroyListeners, ln)
  131. }
  132. func (p *provider) fireDestroy(sid string) {
  133. for _, ln := range p.destroyListeners {
  134. ln(sid)
  135. }
  136. }
  137. // Destroy destroys the session, removes all sessions and flash values,
  138. // the session itself and updates the registered session databases,
  139. // this called from sessionManager which removes the client's cookie also.
  140. func (p *provider) Destroy(sid string) {
  141. p.mu.Lock()
  142. if sess, found := p.sessions[sid]; found {
  143. p.deleteSession(sess)
  144. }
  145. p.mu.Unlock()
  146. }
  147. // DestroyAll removes all sessions
  148. // from the server-side memory (and database if registered).
  149. // Client's session cookie will still exist but it will be reseted on the next request.
  150. func (p *provider) DestroyAll() {
  151. p.mu.Lock()
  152. for _, sess := range p.sessions {
  153. p.deleteSession(sess)
  154. }
  155. p.mu.Unlock()
  156. }
  157. func (p *provider) deleteSession(sess *Session) {
  158. sid := sess.sid
  159. delete(p.sessions, sid)
  160. p.db.Release(sid)
  161. p.fireDestroy(sid)
  162. }
  163. /*
  164. func (p *provider) regenerateID(ctx *context.Context, oldsid string) {
  165. p.mu.RLock()
  166. sess, ok := p.sessions[oldsid]
  167. p.mu.RUnlock()
  168. if ok {
  169. newsid := sess.Man.config.SessionIDGenerator(ctx)
  170. sess.mu.Lock()
  171. sess.sid = newsid
  172. sess.mu.Unlock()
  173. p.mu.Lock()
  174. p.sessions[newsid] = sess
  175. delete(p.sessions, oldsid)
  176. p.mu.Unlock()
  177. }
  178. }
  179. */