| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596 |
- package captcha
- import (
- "container/list"
- "sync"
- "time"
- )
- // expValue stores timestamp and id of captchas. It is used in a list inside
- // store for indexing generated captchas by timestamp to enable garbage
- // collection of expired captchas.
- type expValue struct {
- timestamp int64
- id string
- }
- // store is an internal store for captcha ids and their values.
- type store struct {
- mu sync.RWMutex
- ids map[string][]byte
- exp *list.List
- // Number of items stored after last collection.
- numStored int
- // Number of saved items that triggers collection.
- collectNum int
- // Expiration time of captchas.
- expiration int64
- }
- // newStore initializes and returns a new store.
- func newStore(collectNum int, expiration int64) *store {
- s := new(store)
- s.ids = make(map[string][]byte)
- s.exp = list.New()
- s.collectNum = collectNum
- s.expiration = expiration
- return s
- }
- // saveCaptcha saves the captcha id and the corresponding digits.
- func (s *store) saveCaptcha(id string, digits []byte) {
- s.mu.Lock()
- s.ids[id] = digits
- s.exp.PushBack(expValue{time.Seconds(), id})
- s.numStored++
- s.mu.Unlock()
- if s.numStored > s.collectNum {
- go s.collect()
- }
- }
- // getDigits returns the digits for the given id.
- func (s *store) getDigits(id string) (digits []byte) {
- s.mu.RLock()
- defer s.mu.RUnlock()
- digits, _ = s.ids[id]
- return
- }
- // getDigitsClear returns the digits for the given id, and removes them from
- // the store.
- func (s *store) getDigitsClear(id string) (digits []byte) {
- s.mu.Lock()
- defer s.mu.Unlock()
- digits, ok := s.ids[id]
- if !ok {
- return
- }
- s.ids[id] = nil, false
- // XXX(dchest) Index (s.exp) will be cleaned when collecting expired
- // captchas. Can't clean it here, because we don't store reference to
- // expValue in the map. Maybe store it?
- return
- }
- // collect deletes expired captchas from the store.
- func (s *store) collect() {
- now := time.Seconds()
- s.mu.Lock()
- defer s.mu.Unlock()
- s.numStored = 0
- for e := s.exp.Front(); e != nil; {
- ev, ok := e.Value.(expValue)
- if !ok {
- return
- }
- if ev.timestamp+s.expiration < now {
- s.ids[ev.id] = nil, false
- next := e.Next()
- s.exp.Remove(e)
- e = next
- } else {
- return
- }
- }
- }
|