Possibly fix race condition in rate limiting

This commit is contained in:
Neale Pickett 2019-04-30 03:31:16 +00:00
parent 15ee01b69d
commit 03247983bb
3 changed files with 55 additions and 7 deletions

26
contrib/smash Executable file
View File

@ -0,0 +1,26 @@
#! /bin/sh
BASEURL=http://localhost:8080
case "$1" in
a)
URL=$BASEURL/answer
;;
b)
URL=$BASEURL/puzzles.json
;;
*)
echo "Usage: $0 a|b" 1>&2
exit 1
;;
esac
while true; do
curl \
-X POST \
-F "cat=base" \
-F "points=1" \
-F "id=test" \
-F "answer=moo" \
$URL
done

View File

@ -11,6 +11,7 @@ import (
"os" "os"
"path" "path"
"strings" "strings"
"sync"
"time" "time"
) )
@ -21,12 +22,13 @@ type Instance struct {
ThemeDir string ThemeDir string
AttemptInterval time.Duration AttemptInterval time.Duration
categories map[string]*Mothball categories map[string]*Mothball
update chan bool update chan bool
jPuzzleList []byte jPuzzleList []byte
jPointsLog []byte jPointsLog []byte
nextAttempt map[string]time.Time nextAttempt map[string]time.Time
mux *http.ServeMux nextAttemptMutex *sync.RWMutex
mux *http.ServeMux
} }
func (ctx *Instance) Initialize() error { func (ctx *Instance) Initialize() error {
@ -42,6 +44,7 @@ func (ctx *Instance) Initialize() error {
ctx.categories = map[string]*Mothball{} ctx.categories = map[string]*Mothball{}
ctx.update = make(chan bool, 10) ctx.update = make(chan bool, 10)
ctx.nextAttempt = map[string]time.Time{} ctx.nextAttempt = map[string]time.Time{}
ctx.nextAttemptMutex = new(sync.RWMutex)
ctx.mux = http.NewServeMux() ctx.mux = http.NewServeMux()
ctx.BindHandlers() ctx.BindHandlers()
@ -129,8 +132,15 @@ func (ctx *Instance) ThemePath(parts ...string) string {
func (ctx *Instance) TooFast(teamId string) bool { func (ctx *Instance) TooFast(teamId string) bool {
now := time.Now() now := time.Now()
ctx.nextAttemptMutex.RLock()
next, _ := ctx.nextAttempt[teamId] next, _ := ctx.nextAttempt[teamId]
ctx.nextAttemptMutex.RUnlock()
ctx.nextAttemptMutex.Lock()
ctx.nextAttempt[teamId] = now.Add(ctx.AttemptInterval) ctx.nextAttempt[teamId] = now.Add(ctx.AttemptInterval)
ctx.nextAttemptMutex.Unlock()
return now.Before(next) return now.Before(next)
} }
@ -212,7 +222,10 @@ func (ctx *Instance) OpenCategoryFile(category string, parts ...string) (io.Read
} }
func (ctx *Instance) ValidTeamId(teamId string) bool { func (ctx *Instance) ValidTeamId(teamId string) bool {
ctx.nextAttemptMutex.RLock()
_, ok := ctx.nextAttempt[teamId] _, ok := ctx.nextAttempt[teamId]
ctx.nextAttemptMutex.RUnlock()
return ok return ok
} }

View File

@ -186,19 +186,28 @@ func (ctx *Instance) readTeams() {
now := time.Now() now := time.Now()
added := 0 added := 0
for k, _ := range newList { for k, _ := range newList {
if _, ok := ctx.nextAttempt[k]; !ok { ctx.nextAttemptMutex.RLock()
_, ok := ctx.nextAttempt[k]
ctx.nextAttemptMutex.RUnlock()
if !ok {
ctx.nextAttemptMutex.Lock()
ctx.nextAttempt[k] = now ctx.nextAttempt[k] = now
ctx.nextAttemptMutex.Unlock()
added += 1 added += 1
} }
} }
// For any removed team IDs, remove them // For any removed team IDs, remove them
removed := 0 removed := 0
ctx.nextAttemptMutex.Lock() // XXX: This could be less of a cludgel
for k, _ := range ctx.nextAttempt { for k, _ := range ctx.nextAttempt {
if _, ok := newList[k]; !ok { if _, ok := newList[k]; !ok {
delete(ctx.nextAttempt, k) delete(ctx.nextAttempt, k)
} }
} }
ctx.nextAttemptMutex.Unlock()
if (added > 0) || (removed > 0) { if (added > 0) || (removed > 0) {
log.Printf("Team IDs updated: %d added, %d removed", added, removed) log.Printf("Team IDs updated: %d added, %d removed", added, removed)