store.go 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. package captcha
  2. import (
  3. "container/list"
  4. "sync"
  5. "time"
  6. )
  7. const (
  8. // Expiration time for captchas
  9. Expiration = 2 * 60 // 2 minutes
  10. // The number of captchas created that triggers garbage collection
  11. CollectNum = 100
  12. )
  13. // expValue stores timestamp and id of captchas. It is used in a list inside
  14. // store for indexing generated captchas by timestamp to enable garbage
  15. // collection of expired captchas.
  16. type expValue struct {
  17. timestamp int64
  18. id string
  19. }
  20. // store is an internal store for captcha ids and their values.
  21. type store struct {
  22. mu sync.RWMutex
  23. ids map[string][]byte
  24. exp *list.List
  25. // Number of items stored after last collection
  26. colNum int
  27. }
  28. // newStore initializes and returns a new store.
  29. func newStore() *store {
  30. s := new(store)
  31. s.ids = make(map[string][]byte)
  32. s.exp = list.New()
  33. return s
  34. }
  35. // saveCaptcha saves the captcha id and the corresponding numbers.
  36. func (s *store) saveCaptcha(id string, ns []byte) {
  37. s.mu.Lock()
  38. defer s.mu.Unlock()
  39. s.ids[id] = ns
  40. s.exp.PushBack(expValue{time.Seconds(), id})
  41. s.colNum++
  42. if s.colNum > CollectNum {
  43. go s.collect()
  44. s.colNum = 0
  45. }
  46. }
  47. // getNumbers returns the numbers for the given id.
  48. func (s *store) getNumbers(id string) (ns []byte) {
  49. s.mu.RLock()
  50. defer s.mu.RUnlock()
  51. ns, _ = s.ids[id]
  52. return
  53. }
  54. // getNumbersClear returns the numbers for the given id, and removes them from
  55. // the store.
  56. func (s *store) getNumbersClear(id string) (ns []byte) {
  57. s.mu.Lock()
  58. defer s.mu.Unlock()
  59. ns, ok := s.ids[id]
  60. if !ok {
  61. return
  62. }
  63. s.ids[id] = nil, false
  64. // XXX(dchest) Index (s.exp) will be cleaned when collecting expired
  65. // captchas. Can't clean it here, because we don't store reference to
  66. // expValue in the map. Maybe store it?
  67. return
  68. }
  69. // collect deletes expired captchas from the store.
  70. func (s *store) collect() {
  71. now := time.Seconds()
  72. s.mu.Lock()
  73. defer s.mu.Unlock()
  74. for e := s.exp.Front(); e != nil; e = e.Next() {
  75. ev, ok := e.Value.(expValue)
  76. if !ok {
  77. return
  78. }
  79. if ev.timestamp+Expiration < now {
  80. s.ids[ev.id] = nil, false
  81. s.exp.Remove(e)
  82. } else {
  83. return
  84. }
  85. }
  86. }