all repos — captcha @ fe4a5975321d8c948221aa46f8a7af6355db0612

Go package captcha implements generation and verification of image and audio CAPTCHAs.

store.go (view raw)

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