all repos — captcha @ d36aa1f342df1c83a78ffb1b37d3760eea13c626

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