From 03247983bbc97716c4ff1c13a43f39f6ca0d837d Mon Sep 17 00:00:00 2001 From: Neale Pickett Date: Tue, 30 Apr 2019 03:31:16 +0000 Subject: [PATCH] Possibly fix race condition in rate limiting --- contrib/smash | 26 ++++++++++++++++++++++++++ src/instance.go | 25 +++++++++++++++++++------ src/maintenance.go | 11 ++++++++++- 3 files changed, 55 insertions(+), 7 deletions(-) create mode 100755 contrib/smash diff --git a/contrib/smash b/contrib/smash new file mode 100755 index 0000000..9a743c0 --- /dev/null +++ b/contrib/smash @@ -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 diff --git a/src/instance.go b/src/instance.go index 968e70a..1d3a26f 100644 --- a/src/instance.go +++ b/src/instance.go @@ -11,6 +11,7 @@ import ( "os" "path" "strings" + "sync" "time" ) @@ -21,12 +22,13 @@ type Instance struct { ThemeDir string AttemptInterval time.Duration - categories map[string]*Mothball - update chan bool - jPuzzleList []byte - jPointsLog []byte - nextAttempt map[string]time.Time - mux *http.ServeMux + categories map[string]*Mothball + update chan bool + jPuzzleList []byte + jPointsLog []byte + nextAttempt map[string]time.Time + nextAttemptMutex *sync.RWMutex + mux *http.ServeMux } func (ctx *Instance) Initialize() error { @@ -42,6 +44,7 @@ func (ctx *Instance) Initialize() error { ctx.categories = map[string]*Mothball{} ctx.update = make(chan bool, 10) ctx.nextAttempt = map[string]time.Time{} + ctx.nextAttemptMutex = new(sync.RWMutex) ctx.mux = http.NewServeMux() ctx.BindHandlers() @@ -129,8 +132,15 @@ func (ctx *Instance) ThemePath(parts ...string) string { func (ctx *Instance) TooFast(teamId string) bool { now := time.Now() + + ctx.nextAttemptMutex.RLock() next, _ := ctx.nextAttempt[teamId] + ctx.nextAttemptMutex.RUnlock() + + ctx.nextAttemptMutex.Lock() ctx.nextAttempt[teamId] = now.Add(ctx.AttemptInterval) + ctx.nextAttemptMutex.Unlock() + 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 { + ctx.nextAttemptMutex.RLock() _, ok := ctx.nextAttempt[teamId] + ctx.nextAttemptMutex.RUnlock() + return ok } diff --git a/src/maintenance.go b/src/maintenance.go index 3152c28..abd51e0 100644 --- a/src/maintenance.go +++ b/src/maintenance.go @@ -186,19 +186,28 @@ func (ctx *Instance) readTeams() { now := time.Now() added := 0 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.nextAttemptMutex.Unlock() + added += 1 } } // For any removed team IDs, remove them removed := 0 + ctx.nextAttemptMutex.Lock() // XXX: This could be less of a cludgel for k, _ := range ctx.nextAttempt { if _, ok := newList[k]; !ok { delete(ctx.nextAttempt, k) } } + ctx.nextAttemptMutex.Unlock() if (added > 0) || (removed > 0) { log.Printf("Team IDs updated: %d added, %d removed", added, removed)