Struct's fields made private. expireScanInterval made into a field. SetExpiry() removed. Now go routine for expiry check is in a cleanExpired() private method and being called by a constructor. Implemented a constructor for InMemoryCaptchaDB.

This commit is contained in:
Alexander Andreev 2022-08-19 02:15:34 +04:00
parent b7ba27b613
commit 7737c360d0
Signed by: Arav
GPG Key ID: 0388CC8FAA51063F

View File

@ -12,7 +12,7 @@ import (
var errorNotFound = errors.New("captcha not found") var errorNotFound = errors.New("captcha not found")
var expiredScanInterval = 60 * time.Second var defaultExpiredScanInterval = 60 * time.Second
type ID string type ID string
@ -29,19 +29,36 @@ func NewID(additionalData string, answer Answer) ID {
} }
type CaptchaDB interface { type CaptchaDB interface {
New(data string) (Captcha, ID) New(data string, captcha Captcha) (Captcha, ID)
SetExpiry(expiry time.Duration)
GetExpiry() time.Duration GetExpiry() time.Duration
Image(id ID) (*image.Image, error) Image(id ID, style string) (*image.Image, error)
Solve(id ID, answer Answer) (bool, error) Solve(id ID, answer Answer) (bool, error)
IsSolved(id ID) bool IsSolved(id ID) (bool, error)
cleanExpired()
} }
type InMemoryCaptchaDB struct { type InMemoryCaptchaDB struct {
sync.Mutex sync.Mutex
DB map[ID]Captcha db map[ID]Captcha
ExpireIn time.Duration expireIn time.Duration
expireScanInterval time.Duration
}
func NewInMemoryCaptchaDB(expire time.Duration) *InMemoryCaptchaDB {
db := &InMemoryCaptchaDB{
db: make(map[ID]Captcha),
expireIn: expire}
if expire < defaultExpiredScanInterval {
db.expireScanInterval = expire
} else {
db.expireScanInterval = defaultExpiredScanInterval
}
db.cleanExpired()
return db
} }
// New accepts an Captcha instance, generates an ID and store it in a database. // New accepts an Captcha instance, generates an ID and store it in a database.
@ -51,29 +68,23 @@ func (cdb *InMemoryCaptchaDB) New(data string, captcha Captcha) (Captcha, ID) {
id := NewID(data, captcha.GetAnswer()) id := NewID(data, captcha.GetAnswer())
cdb.Lock() cdb.Lock()
cdb.DB[id] = captcha cdb.db[id] = captcha
cdb.Unlock() cdb.Unlock()
return captcha, id return captcha, id
} }
// SetExpiry stores expire value and starts a goroutine // cleanExpired starts a goroutine that deletes expired CAPTCHAs.
// that checks for expired CAPTCHAs. func (cdb *InMemoryCaptchaDB) cleanExpired() {
func (cdb *InMemoryCaptchaDB) SetExpiry(expire time.Duration) {
cdb.ExpireIn = expire
if expire < expiredScanInterval {
expiredScanInterval = expire
}
go func() { go func() {
for { for {
sleepFor := expiredScanInterval - (time.Duration(time.Now().Second()) % expiredScanInterval) sleepFor := cdb.expireScanInterval - (time.Duration(time.Now().Second()) % cdb.expireScanInterval)
time.Sleep(sleepFor) time.Sleep(sleepFor)
cdb.Lock() cdb.Lock()
for id, captcha := range cdb.DB { for id, captcha := range cdb.db {
if time.Since(captcha.Expiry()) >= cdb.ExpireIn { if time.Since(captcha.Expiry()) >= cdb.expireIn {
delete(cdb.DB, id) delete(cdb.db, id)
} }
} }
cdb.Unlock() cdb.Unlock()
@ -83,14 +94,14 @@ func (cdb *InMemoryCaptchaDB) SetExpiry(expire time.Duration) {
// GetExpiry returns time for how long captcha will last. // GetExpiry returns time for how long captcha will last.
func (cdb *InMemoryCaptchaDB) GetExpiry() time.Duration { func (cdb *InMemoryCaptchaDB) GetExpiry() time.Duration {
return cdb.ExpireIn return cdb.expireIn
} }
// Image returns image for a captcha. // Image returns image for a captcha.
func (cdb *InMemoryCaptchaDB) Image(id ID, style string) (*image.Image, error) { func (cdb *InMemoryCaptchaDB) Image(id ID, style string) (*image.Image, error) {
cdb.Lock() cdb.Lock()
defer cdb.Unlock() defer cdb.Unlock()
if c, ok := cdb.DB[id]; ok { if c, ok := cdb.db[id]; ok {
return c.Image(style), nil return c.Image(style), nil
} }
return nil, errorNotFound return nil, errorNotFound
@ -101,10 +112,10 @@ func (cdb *InMemoryCaptchaDB) Image(id ID, style string) (*image.Image, error) {
func (cdb *InMemoryCaptchaDB) Solve(id ID, answer Answer) (bool, error) { func (cdb *InMemoryCaptchaDB) Solve(id ID, answer Answer) (bool, error) {
cdb.Lock() cdb.Lock()
defer cdb.Unlock() defer cdb.Unlock()
if c, ok := cdb.DB[id]; ok { if c, ok := cdb.db[id]; ok {
ok = c.Solve(answer) ok = c.Solve(answer)
if !ok { if !ok {
delete(cdb.DB, id) delete(cdb.db, id)
} }
return ok, nil return ok, nil
} }
@ -116,9 +127,9 @@ func (cdb *InMemoryCaptchaDB) Solve(id ID, answer Answer) (bool, error) {
func (cdb *InMemoryCaptchaDB) IsSolved(id ID) (bool, error) { func (cdb *InMemoryCaptchaDB) IsSolved(id ID) (bool, error) {
cdb.Lock() cdb.Lock()
defer cdb.Unlock() defer cdb.Unlock()
if c, ok := cdb.DB[id]; ok { if c, ok := cdb.db[id]; ok {
ok = c.IsSolved() ok = c.IsSolved()
delete(cdb.DB, id) delete(cdb.db, id)
return ok, nil return ok, nil
} }
return false, errorNotFound return false, errorNotFound