Compare commits

..

3 Commits

Author SHA1 Message Date
Arne Maier 82790e260d Add log 2024-11-11 21:52:33 +01:00
Arne Maier 47ed047f7e Add throttle to context 2024-11-11 21:43:12 +01:00
Arne Maier ac5837e26a Correct context 2024-11-11 21:34:30 +01:00
2 changed files with 37 additions and 19 deletions

46
main.go
View File

@ -5,20 +5,37 @@ import (
"fmt" "fmt"
"github.com/kordondev/meeting/throttling" "github.com/kordondev/meeting/throttling"
"net/http" "net/http"
"slices"
"time" "time"
) )
const burstLimit = 2 const burstLimit = 2
func main() { func main() {
throttles := make(map[string]<-chan time.Time) ctx := context.Background()
calledLastHour := make([]string, 0) throttles := make(map[string]context.Context) // use sync map
http.HandleFunc("/", serveIndexFile) http.HandleFunc("/", serveIndexFile)
http.HandleFunc("/input.txt", serveInputFile) http.HandleFunc("/input.txt", serveInputFile)
http.HandleFunc("/result", func(w http.ResponseWriter, r *http.Request) { http.HandleFunc("/result", func(w http.ResponseWriter, r *http.Request) {
tryResult(w, r, throttles, calledLastHour)
cctxV, ok := throttles[r.RemoteAddr]
if !ok {
cctx, cancel := context.WithCancel(ctx)
throttle := throttling.CreateThrottle(cctx, burstLimit)
a := throttleWithCancel{
throttle: throttle,
cancel: cancel,
}
cctxV = context.WithValue(ctx, "throttle", a)
throttles[r.RemoteAddr] = cctxV
go func() {
time.Sleep(time.Minute * 20)
delete(throttles, r.RemoteAddr)
cancel()
}()
}
tryResult(cctxV, w, r)
}) })
err := http.ListenAndServe(":3333", nil) err := http.ListenAndServe(":3333", nil)
@ -27,6 +44,11 @@ func main() {
} }
} }
type throttleWithCancel struct {
throttle <-chan time.Time
cancel context.CancelFunc
}
func serveIndexFile(w http.ResponseWriter, r *http.Request) { func serveIndexFile(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "static/index-with-text.html") http.ServeFile(w, r, "static/index-with-text.html")
} }
@ -36,26 +58,18 @@ func serveInputFile(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "static/input.txt") http.ServeFile(w, r, "static/input.txt")
} }
func tryResult(w http.ResponseWriter, r *http.Request, throttles map[string]<-chan time.Time, calledLastHour []string) { func tryResult(ctx context.Context, w http.ResponseWriter, r *http.Request) {
clientIP := r.RemoteAddr clientIP := r.RemoteAddr
clientResult := r.URL.Query().Get("result") clientResult := r.URL.Query().Get("result")
fmt.Println(clientIP, clientResult) fmt.Println(clientIP, "called with:", clientResult)
if slices.Contains(calledLastHour, clientIP) { throttle := ctx.Value("throttle").(throttleWithCancel).throttle
calledLastHour = append(calledLastHour, clientIP)
}
throttle, ok := throttles[clientIP]
if !ok {
fmt.Println("Creating new throttle")
throttle = throttling.CreateThrottle(r.Context(), burstLimit)
throttles[clientIP] = throttle
}
payload := throttling.Payload{ payload := throttling.Payload{
R: r, R: r,
W: w, W: w,
ClientResult: clientResult, ClientResult: clientResult,
} }
throttling.CallFunction(context.TODO(), &CheckResult{}, &payload, throttle) throttling.CallFunction(ctx, &CheckResult{}, &payload, throttle)
} }
type CheckResult struct { type CheckResult struct {

View File

@ -24,9 +24,6 @@ type Payload struct {
// CallFunction allows burst rate limiting client calls with the // CallFunction allows burst rate limiting client calls with the
// payloads. // payloads.
func CallFunction(ctx context.Context, client Client, payload *Payload, throttle <-chan time.Time) { func CallFunction(ctx context.Context, client Client, payload *Payload, throttle <-chan time.Time) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
<-throttle // rate limit our client calls <-throttle // rate limit our client calls
client.Call(payload) client.Call(payload)
} }
@ -43,11 +40,18 @@ func CreateThrottle(ctx context.Context, burstLimit int) <-chan time.Time {
for t := range ticker.C { for t := range ticker.C {
select { select {
case throttle <- t: case throttle <- t:
{
fmt.Println("Add bucket to throttle")
}
case <-ctx.Done(): case <-ctx.Done():
{ {
fmt.Println("Ticker done") fmt.Println("Ticker done")
return // exit goroutine when surrounding function returns return // exit goroutine when surrounding function returns
} }
default:
{
fmt.Println("Dropping bucket")
}
} }
} }
}() }()